123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
SPDX-License-Identifier: ISC
---------------------------------------------------------------------------*)letrev_comparen0n1=comparen1n0letstrf=Printf.sprintfletorder_argsa0a1=matchCmdliner_info.Arg.is_opta0,Cmdliner_info.Arg.is_opta1with|true,true->(* optional by name *)letkeynames=letk=List.hd(List.sortrev_comparenames)inletk=String.lowercase_asciikinifk.[1]='-'thenString.subk1(String.lengthk-1)elsekincompare(key@@Cmdliner_info.Arg.opt_namesa0)(key@@Cmdliner_info.Arg.opt_namesa1)|false,false->(* positional by variable *)compare(String.lowercase_ascii@@Cmdliner_info.Arg.docva0)(String.lowercase_ascii@@Cmdliner_info.Arg.docva1)|true,false->-1(* positional first *)|false,true->1(* optional after *)letesc=Cmdliner_manpage.escapeletcmd_namet=esc@@Cmdliner_info.Cmd.nametletsorted_items_to_blocks~boilerplate:bitems=(* 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 *)letboilerplate=matchbwithNone->(fun_->None)|Someb->binletmk_blocksecacc=matchboilerplatesecwith|None->(sec,`Blocksacc)|Someb->(sec,`Blocks(b::acc))inletrecloopsecssecacc=function|(sec',it)::itswhensec'=sec->loopsecssec(it::acc)its|(sec',it)::its->loop(mk_blocksecacc::secs)sec'[it]its|[]->(mk_blocksecacc)::secsinmatchitemswith|[]->[]|(sec,it)::its->loop[]sec[it]its(* Doc string variables substitutions. *)letenv_info_subst~subste=function|"env"->Some(strf"$(b,%s)"@@esc(Cmdliner_info.Env.info_vare))|id->substidletexit_info_subst~subste=function|"status"->Some(strf"%d"(fst@@Cmdliner_info.Exit.info_codese))|"status_max"->Some(strf"%d"(snd@@Cmdliner_info.Exit.info_codese))|id->substidletarg_info_subst~substa=function|"docv"->Some(strf"$(i,%s)"@@esc(Cmdliner_info.Arg.docva))|"opt"whenCmdliner_info.Arg.is_opta->Some(strf"$(b,%s)"@@esc(Cmdliner_info.Arg.opt_name_samplea))|"env"asid->beginmatchCmdliner_info.Arg.envawith|Somee->env_info_subst~substeid|None->substidend|id->substidletcmd_info_substei=function|"tname"->Some(strf"$(b,%s)"@@cmd_name(Cmdliner_info.Eval.cmdei))|"mname"->Some(strf"$(b,%s)"@@cmd_name(Cmdliner_info.Eval.mainei))|"iname"->letcmd=Cmdliner_info.Eval.cmdei::Cmdliner_info.Eval.parentseiinletcmd=String.concat" "(List.rev_mapCmdliner_info.Cmd.namecmd)inSome(strf"$(b,%s)"cmd)|_->None(* Command docs *)letinvocation?(sep=" ")?(parents=[])cmd=letnames=List.rev_mapCmdliner_info.Cmd.name(cmd::parents)inesc@@String.concatsepnamesletsynopsis_pos_arga=letv=matchCmdliner_info.Arg.docvawith""->"ARG"|v->vinletv=strf"$(i,%s)"(escv)inletv=(ifCmdliner_info.Arg.is_reqathenstrf"%s"elsestrf"[%s]")vinmatchCmdliner_info.Arg.(pos_len@@pos_kinda)with|None->v^"…"|Some1->v|Somen->letrecloopnacc=ifn<=0thenaccelseloop(n-1)(v::acc)inString.concat" "(loopn[])letsynopsis_opt_argan=letvar=matchCmdliner_info.Arg.docvawith""->"VAL"|v->vinmatchCmdliner_info.Arg.opt_kindawith|Cmdliner_info.Arg.Flag->strf"$(b,%s)"(escn)|Cmdliner_info.Arg.Opt->ifString.lengthn>2thenstrf"$(b,%s)=$(i,%s)"(escn)(escvar)elsestrf"$(b,%s) $(i,%s)"(escn)(escvar)|Cmdliner_info.Arg.Opt_vopt_->ifString.lengthn>2thenstrf"$(b,%s)[=$(i,%s)]"(escn)(escvar)elsestrf"$(b,%s) [$(i,%s)]"(escn)(escvar)letdeprecatedcmd=matchCmdliner_info.Cmd.deprecatedcmdwith|None->""|Some_->"(Deprecated) "letsynopsis?parentscmd=matchCmdliner_info.Cmd.childrencmdwith|[]->letrev_cli_order(a0,_)(a1,_)=Cmdliner_info.Arg.rev_pos_cli_ordera0a1inletargs=Cmdliner_info.Cmd.argscmdinletoargs,pargs=Cmdliner_info.Arg.(Set.partitionis_optargs)inletoargs=(* Keep only those that are listed in the s_options section and
that are not [--version] or [--help]. * *)letkeepa=letdrop_namesn=n="--help"||n="--version"inCmdliner_info.Arg.docsa=Cmdliner_manpage.s_options&¬(List.existsdrop_names(Cmdliner_info.Arg.opt_namesa))inletoargs=Cmdliner_info.Arg.Set.(elements(filterkeepoargs))inletcount=List.lengthoargsinletany_option="[$(i,OPTION)]…"inifcount=0||count>3thenany_optionelseletsyna=strf"[%s]"(synopsis_opt_arga(Cmdliner_info.Arg.opt_name_samplea))inletoargs=List.sortorder_argsoargsinletoargs=String.concat" "(List.mapsynoargs)inString.concat" "[oargs;any_option]inletpargs=letpargs=Cmdliner_info.Arg.Set.elementspargsinifpargs=[]then""elseletpargs=List.map(funa->a,synopsis_pos_arga)pargsinletpargs=List.sortrev_cli_orderpargsinString.concat" "(""(* add a space *)::List.rev_mapsndpargs)instrf"%s$(b,%s) %s%s"(deprecatedcmd)(invocation?parentscmd)oargspargs|_cmds->letsubcmd=matchCmdliner_info.Cmd.has_argscmdwith|false->"$(i,COMMAND)"|true->"[$(i,COMMAND)]"instrf"%s$(b,%s) %s …"(deprecatedcmd)(invocation?parentscmd)subcmdletcmd_docsei=matchCmdliner_info.(Cmd.children(Eval.cmdei))with|[]->[]|cmds->letadd_cmdacccmd=letsyn=synopsiscmdin(Cmdliner_info.Cmd.docscmd,`I(syn,Cmdliner_info.Cmd.doccmd))::accinletby_sec_by_rev_name(s0,`I(c0,_))(s1,`I(c1,_))=letc=compares0s1inifc<>0thencelsecomparec1c0(* N.B. reverse *)inletcmds=List.fold_leftadd_cmd[]cmdsinletcmds=List.sortby_sec_by_rev_namecmdsinletcmds=(cmds:>(string*Cmdliner_manpage.block)list)insorted_items_to_blocks~boilerplate:Nonecmds(* Argument docs *)letarg_man_item_labela=lets=matchCmdliner_info.Arg.is_posawith|true->strf"$(i,%s)"(esc@@Cmdliner_info.Arg.docva)|false->letnames=List.sortcompare(Cmdliner_info.Arg.opt_namesa)inString.concat", "(List.rev_map(synopsis_opt_arga)names)inmatchCmdliner_info.Arg.deprecatedawith|None->s|Some_->"(Deprecated) "^sletarg_to_man_item~errs~subst~bufa=letsubst=arg_info_subst~substainletor_env~valuea=matchCmdliner_info.Arg.envawith|None->""|Somee->letvalue=ifvaluethen" or"else"absent "instrf"%s $(b,%s) env"value(esc@@Cmdliner_info.Env.info_vare)inletabsent=matchCmdliner_info.Arg.absentawith|Cmdliner_info.Arg.Err->"required"|Cmdliner_info.Arg.Doc""->strf"%s"(or_env~value:falsea)|Cmdliner_info.Arg.Docs->lets=Cmdliner_manpage.subst_vars~errs~substbufsinstrf"absent=%s%s"s(or_env~value:truea)|Cmdliner_info.Arg.Valv->matchLazy.forcevwith|""->strf"%s"(or_env~value:falsea)|v->strf"absent=$(b,%s)%s"(escv)(or_env~value:truea)inletoptvopt=matchCmdliner_info.Arg.opt_kindawith|Cmdliner_info.Arg.Opt_voptv->strf"default=$(b,%s)"(escv)|_->""inletargvdoc=matchoptvopt,absentwith|"",""->""|s,""|"",s->strf" (%s)"s|s,s'->strf" (%s) (%s)"ss'inletdoc=Cmdliner_info.Arg.docainletdoc=Cmdliner_manpage.subst_vars~errs~substbufdocin(Cmdliner_info.Arg.docsa,`I(arg_man_item_labela^argvdoc,doc))letarg_docs~errs~subst~bufei=letby_sec_by_arga0a1=letc=compare(Cmdliner_info.Arg.docsa0)(Cmdliner_info.Arg.docsa1)inifc<>0thencelseletc=matchCmdliner_info.Arg.deprecateda0,Cmdliner_info.Arg.deprecateda1with|None,None|Some_,Some_->0|None,Some_->-1|Some_,None->1inifc<>0thencelseorder_argsa0a1inletkeep_argaacc=ifnotCmdliner_info.Arg.(is_posa&&(docva=""||doca=""))then(a::acc)elseaccinletargs=Cmdliner_info.Cmd.args@@Cmdliner_info.Eval.cmdeiinletargs=Cmdliner_info.Arg.Set.foldkeep_argargs[]inletargs=List.sortby_sec_by_argargsinletargs=List.rev_map(arg_to_man_item~errs~subst~buf)argsinsorted_items_to_blocks~boilerplate:Noneargs(* Exit statuses doc *)letexit_boilerplatesec=matchsec=Cmdliner_manpage.s_exit_statuswith|false->None|true->Some(Cmdliner_manpage.s_exit_status_intro)letexit_docs~errs~subst~buf~has_sexitei=letby_sec(s0,_)(s1,_)=compares0s1inletadd_exit_itemacce=letsubst=exit_info_subst~substeinletmin,max=Cmdliner_info.Exit.info_codeseinletdoc=Cmdliner_info.Exit.info_doceinletlabel=ifmin=maxthenstrf"%d"minelsestrf"%d-%d"minmaxinletitem=`I(label,Cmdliner_manpage.subst_vars~errs~substbufdoc)in(Cmdliner_info.Exit.info_docse,item)::accinletexits=Cmdliner_info.Cmd.exits@@Cmdliner_info.Eval.cmdeiinletexits=List.sortCmdliner_info.Exit.info_orderexitsinletexits=List.fold_leftadd_exit_item[]exitsinletexits=List.stable_sortby_sec(* sort by section *)exitsinletboilerplate=ifhas_sexitthenNoneelseSomeexit_boilerplateinsorted_items_to_blocks~boilerplateexits(* Environment doc *)letenv_boilerplatesec=matchsec=Cmdliner_manpage.s_environmentwith|false->None|true->Some(Cmdliner_manpage.s_environment_intro)letenv_docs~errs~subst~buf~has_senvei=letadd_env_item~subst(seen,envsasacc)e=ifCmdliner_info.Env.Set.memeseenthenaccelseletseen=Cmdliner_info.Env.Set.addeseeninletvar=strf"$(b,%s)"@@esc(Cmdliner_info.Env.info_vare)inletvar=matchCmdliner_info.Env.info_deprecatedewith|None->var|Some_->"(Deprecated) "^varinletdoc=Cmdliner_info.Env.info_doceinletdoc=Cmdliner_manpage.subst_vars~errs~substbufdocinletenvs=(Cmdliner_info.Env.info_docse,`I(var,doc))::envsinseen,envsinletadd_arg_envaacc=matchCmdliner_info.Arg.envawith|None->acc|Somee->add_env_item~subst:(arg_info_subst~substa)acceinletadd_envacce=add_env_item~subst:(env_info_subst~subste)acceinletby_sec_by_rev_name(s0,`I(v0,_))(s1,`I(v1,_))=letc=compares0s1inifc<>0thencelsecomparev1v0(* 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. *)letargs=Cmdliner_info.Cmd.args@@Cmdliner_info.Eval.cmdeiinlettenvs=Cmdliner_info.Cmd.envs@@Cmdliner_info.Eval.cmdeiinletinit=Cmdliner_info.Env.Set.empty,[]inletacc=Cmdliner_info.Arg.Set.foldadd_arg_envargsinitinlet_,envs=List.fold_leftadd_envacctenvsinletenvs=List.sortby_sec_by_rev_nameenvsinletenvs=(envs:>(string*Cmdliner_manpage.block)list)inletboilerplate=ifhas_senvthenNoneelseSomeenv_boilerplateinsorted_items_to_blocks~boilerplateenvs(* xref doc *)letxref_docs~errsei=letmain=Cmdliner_info.Eval.maineiinletto_xref=function|`Main->Cmdliner_info.Cmd.namemain,1|`Tooltool->tool,1|`Page(name,sec)->name,sec|`Cmdc->(* N.B. we are handling only the first subcommand level here *)letcmds=Cmdliner_info.Cmd.childrenmaininletmname=Cmdliner_info.Cmd.namemaininletis_cmdcmd=Cmdliner_info.Cmd.namecmd=cinifList.existsis_cmdcmdsthenstrf"%s-%s"mnamec,1else(Format.fprintferrs"xref %s: no such command name@."c;"doc-err",0)inletxref_str(name,sec)=strf"%s(%d)"(escname)secinletxrefs=Cmdliner_info.Cmd.man_xrefs@@Cmdliner_info.Eval.cmdeiinletxrefs=matchmain==Cmdliner_info.Eval.cmdeiwith|true->List.filter(funx->x<>`Main)xrefs(* filter out default *)|false->xrefsinletxrefs=List.fold_left(funaccx->to_xrefx::acc)[]xrefsinletxrefs=List.(rev_mapxref_str(sortrev_comparexrefs))inifxrefs=[]then[]else[Cmdliner_manpage.s_see_also,`P(String.concat", "xrefs)](* Man page construction *)letensure_s_nameeism=ifCmdliner_manpage.(smap_has_sectionsm~sec:s_name)thensmelseletcmd=Cmdliner_info.Eval.cmdeiinletparents=Cmdliner_info.Eval.parentseiinlettname=(deprecatedcmd)^invocation~sep:"-"~parentscmdinlettdoc=Cmdliner_info.Cmd.doccmdinlettagline=iftdoc=""then""elsestrf" - %s"tdocinlettagline=`P(strf"%s%s"tnametagline)inCmdliner_manpage.(smap_append_blocksm~sec:s_nametagline)letensure_s_synopsiseism=ifCmdliner_manpage.(smap_has_sectionsm~sec:s_synopsis)thensmelseletcmd=Cmdliner_info.Eval.cmdeiinletparents=Cmdliner_info.Eval.parentseiinletsynopsis=`P(synopsis~parentscmd)inCmdliner_manpage.(smap_append_blocksm~sec:s_synopsissynopsis)letinsert_cmd_man_docs~errseism=letbuf=Buffer.create200inletsubst=cmd_info_substeiinletinssm(sec,b)=Cmdliner_manpage.smap_append_blocksm~secbinlethas_senv=Cmdliner_manpage.(smap_has_sectionsm~sec:s_environment)inlethas_sexit=Cmdliner_manpage.(smap_has_sectionsm~sec:s_exit_status)inletsm=List.fold_leftinssm(cmd_docsei)inletsm=List.fold_leftinssm(arg_docs~errs~subst~bufei)inletsm=List.fold_leftinssm(exit_docs~errs~subst~buf~has_sexitei)inletsm=List.fold_leftinssm(env_docs~errs~subst~buf~has_senvei)inletsm=List.fold_leftinssm(xref_docs~errsei)insmlettext~errsei=letman=Cmdliner_info.Cmd.man@@Cmdliner_info.Eval.cmdeiinletsm=Cmdliner_manpage.smap_of_blocksmaninletsm=ensure_s_nameeisminletsm=ensure_s_synopsiseisminletsm=insert_cmd_man_docsei~errssminCmdliner_manpage.smap_to_blockssmlettitleei=letmain=Cmdliner_info.Eval.maineiinletexec=String.capitalize_ascii(Cmdliner_info.Cmd.namemain)inletcmd=Cmdliner_info.Eval.cmdeiinletparents=Cmdliner_info.Eval.parentseiinletname=String.uppercase_ascii(invocation~sep:"-"~parentscmd)inletcenter_header=esc@@strf"%s Manual"execinletleft_footer=letversion=matchCmdliner_info.Cmd.versionmainwith|None->""|Somev->" "^vinesc@@strf"%s%s"execversioninname,1,"",left_footer,center_headerletman~errsei=titleei,text~errseiletpp_man~errsfmtppfei=Cmdliner_manpage.print~errs~subst:(cmd_info_substei)fmtppf(man~errsei)(* Plain synopsis for usage *)letpp_plain_synopsis~errsppfei=letbuf=Buffer.create100inletsubst=cmd_info_substeiinletcmd=Cmdliner_info.Eval.cmdeiinletparents=Cmdliner_info.Eval.parentseiinletsynopsis=synopsis~parentscmdinletsyn=Cmdliner_manpage.doc_to_plain~errs~substbufsynopsisinFormat.fprintfppf"@[%s@]"syn