123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407(**************************************************************************)(* *)(* OCaml *)(* *)(* Damien Doligez, projet Para, INRIA Rocquencourt *)(* *)(* Copyright 1996 Institut National de Recherche en Informatique et *)(* en Automatique. *)(* *)(* All rights reserved. This file is distributed under the terms of *)(* the GNU Lesser General Public License version 2.1, with the *)(* special exception on linking described in the file LICENSE. *)(* *)(**************************************************************************)typekey=stringtypedoc=stringtypeusage_msg=stringtypeanon_fun=(string->unit)typespec=|Unitof(unit->unit)(* Call the function with unit argument *)|Boolof(bool->unit)(* Call the function with a bool argument *)|Setofboolref(* Set the reference to true *)|Clearofboolref(* Set the reference to false *)|Stringof(string->unit)(* Call the function with a string argument *)|Set_stringofstringref(* Set the reference to the string argument *)|Intof(int->unit)(* Call the function with an int argument *)|Set_intofintref(* Set the reference to the int argument *)|Floatof(float->unit)(* Call the function with a float argument *)|Set_floatoffloatref(* Set the reference to the float argument *)|Tupleofspeclist(* Take several arguments according to the
spec list *)|Symbolofstringlist*(string->unit)(* Take one of the symbols as argument and
call the function with the symbol. *)|Restof(string->unit)(* Stop interpreting keywords and call the
function with each remaining argument *)|Expandof(string->stringarray)(* If the remaining arguments to process
are of the form
[["-foo"; "arg"] @ rest] where "foo"
is registered as [Expand f], then the
arguments [f "arg" @ rest] are
processed. Only allowed in
[parse_and_expand_argv_dynamic]. *)exceptionBadofstringexceptionHelpofstringtypeerror=|Unknownofstring|Wrongofstring*string*string(* option, actual, expected *)|Missingofstring|MessageofstringexceptionStopoferror(* used internally *)openPrintfletrecassoc3xl=matchlwith|[]->raiseNot_found|(y1,y2,_)::_wheny1=x->y2|_::t->assoc3xtletsplits=leti=String.indexs'='inletlen=String.lengthsinString.subs0i,String.subs(i+1)(len-(i+1))letmake_symlistprefixsepsuffixl=matchlwith|[]->"<none>"|h::t->(List.fold_left(funxy->x^sep^y)(prefix^h)t)^suffixletprint_specbuf(key,spec,doc)=ifString.lengthdoc>0thenmatchspecwith|Symbol(l,_)->bprintfbuf" %s %s%s\n"key(make_symlist"{""|""}"l)doc|_->bprintfbuf" %s %s\n"keydoclethelp_action()=raise(Stop(Unknown"-help"))letadd_helpspeclist=letadd1=tryignore(assoc3"-help"speclist);[]withNot_found->["-help",Unithelp_action," Display this list of options"]andadd2=tryignore(assoc3"--help"speclist);[]withNot_found->["--help",Unithelp_action," Display this list of options"]inspeclist@(add1@add2)letusage_bbufspeclisterrmsg=bprintfbuf"%s\n"errmsg;List.iter(print_specbuf)(add_helpspeclist)letusage_stringspeclisterrmsg=letb=Buffer.create200inusage_bbspeclisterrmsg;Buffer.contentsbletusagespeclisterrmsg=eprintf"%s"(usage_stringspeclisterrmsg)letcurrent=ref0letbool_of_string_optx=trySome(bool_of_stringx)withInvalid_argument_->Noneletint_of_string_optx=trySome(int_of_stringx)withFailure_->Noneletfloat_of_string_optx=trySome(float_of_stringx)withFailure_->Noneletparse_and_expand_argv_dynamic_auxallow_expandcurrentargvspeclistanonfunerrmsg=letinitpos=!currentinletconvert_errorerror=(* convert an internal error to a Bad/Help exception
*or* add the program name as a prefix and the usage message as a suffix
to an user-raised Bad exception.
*)letb=Buffer.create200inletprogname=ifinitpos<(Array.length!argv)then!argv.(initpos)else"(?)"inbeginmatcherrorwith|Unknown"-help"->()|Unknown"--help"->()|Unknowns->bprintfb"%s: unknown option '%s'.\n"prognames|Missings->bprintfb"%s: option '%s' needs an argument.\n"prognames|Wrong(opt,arg,expected)->bprintfb"%s: wrong argument '%s'; option '%s' expects %s.\n"prognameargoptexpected|Messages->(* user error message *)bprintfb"%s: %s.\n"prognamesend;usage_bb!speclisterrmsg;iferror=Unknown"-help"||error=Unknown"--help"thenHelp(Buffer.contentsb)elseBad(Buffer.contentsb)inincrcurrent;while!current<(Array.length!argv)dobegintrylets=!argv.(!current)inifString.lengths>=1&&s.[0]='-'thenbeginletaction,follow=tryassoc3s!speclist,NonewithNot_found->tryletkeyword,arg=splitsinassoc3keyword!speclist,SomeargwithNot_found->raise(Stop(Unknowns))inletno_arg()=matchfollowwith|None->()|Somearg->raise(Stop(Wrong(s,arg,"no argument")))inletget_arg()=matchfollowwith|None->if!current+1<(Array.length!argv)then!argv.(!current+1)elseraise(Stop(Missings))|Somearg->arginletconsume_arg()=matchfollowwith|None->incrcurrent|Some_->()inletrectreat_action=function|Unitf->no_arg();f();|Boolf->letarg=get_arg()inbeginmatchbool_of_string_optargwith|None->raise(Stop(Wrong(s,arg,"a boolean")))|Somes->fsend;consume_arg();|Setr->no_arg();r:=true;|Clearr->no_arg();r:=false;|Stringf->letarg=get_arg()infarg;consume_arg();|Symbol(symb,f)->letarg=get_arg()inifList.memargsymbthenbeginfarg;consume_arg();endelsebeginraise(Stop(Wrong(s,arg,"one of: "^(make_symlist""" """symb))))end|Set_stringr->r:=get_arg();consume_arg();|Intf->letarg=get_arg()inbeginmatchint_of_string_optargwith|None->raise(Stop(Wrong(s,arg,"an integer")))|Somex->fxend;consume_arg();|Set_intr->letarg=get_arg()inbeginmatchint_of_string_optargwith|None->raise(Stop(Wrong(s,arg,"an integer")))|Somex->r:=xend;consume_arg();|Floatf->letarg=get_arg()inbeginmatchfloat_of_string_optargwith|None->raise(Stop(Wrong(s,arg,"a float")))|Somex->fxend;consume_arg();|Set_floatr->letarg=get_arg()inbeginmatchfloat_of_string_optargwith|None->raise(Stop(Wrong(s,arg,"a float")))|Somex->r:=xend;consume_arg();|Tuplespecs->no_arg();List.itertreat_actionspecs;|Restf->no_arg();while!current<(Array.length!argv)-1dof!argv.(!current+1);consume_arg();done;|Expandf->ifnotallow_expandthenraise(Invalid_argument"Arg.Expand is is only allowed with \
Arg.parse_and_expand_argv_dynamic");letarg=get_arg()inletnewarg=farginconsume_arg();letbefore=Array.sub!argv0(!current+1)andafter=Array.sub!argv(!current+1)((Array.length!argv)-!current-1)inargv:=Array.concat[before;newarg;after];intreat_actionactionendelseanonfunswith|Badm->raise(convert_error(Messagem));|Stope->raise(convert_errore);end;incrcurrentdoneletparse_and_expand_argv_dynamiccurrentargvspeclistanonfunerrmsg=parse_and_expand_argv_dynamic_auxtruecurrentargvspeclistanonfunerrmsgletparse_argv_dynamic?(current=current)argvspeclistanonfunerrmsg=parse_and_expand_argv_dynamic_auxfalsecurrent(refargv)speclistanonfunerrmsgletparse_argv?(current=current)argvspeclistanonfunerrmsg=parse_argv_dynamic~current:currentargv(refspeclist)anonfunerrmsgletparselfmsg=tryparse_argvSys.argvlfmsgwith|Badmsg->eprintf"%s"msg;exit2|Helpmsg->printf"%s"msg;exit0letparse_dynamiclfmsg=tryparse_argv_dynamicSys.argvlfmsgwith|Badmsg->eprintf"%s"msg;exit2|Helpmsg->printf"%s"msg;exit0letparse_expandlfmsg=tryletargv=refSys.argvinletspec=reflinletcurrent=ref(!current)inparse_and_expand_argv_dynamiccurrentargvspecfmsgwith|Badmsg->eprintf"%s"msg;exit2|Helpmsg->printf"%s"msg;exit0letsecond_words=letlen=String.lengthsinletrecloopn=ifn>=lenthenlenelseifs.[n]=' 'thenloop(n+1)elseninmatchString.indexs'\t'with|n->loop(n+1)|exceptionNot_found->beginmatchString.indexs' 'with|n->loop(n+1)|exceptionNot_found->lenendletmax_arg_lencur(kwd,spec,doc)=matchspecwith|Symbol_->maxcur(String.lengthkwd)|_->maxcur(String.lengthkwd+second_worddoc)letreplace_leading_tabs=letseen=reffalseinString.map(function'\t'whennot!seen->seen:=true;' '|c->c)sletadd_paddinglenksd=matchksdwith|(_,_,"")->(* Do not pad undocumented options, so that they still don't show up when
* run through [usage] or [parse]. *)ksd|(kwd,(Symbol_asspec),msg)->letcutcol=second_wordmsginletspaces=String.make((max0(len-cutcol))+3)' 'in(kwd,spec,"\n"^spaces^replace_leading_tabmsg)|(kwd,spec,msg)->letcutcol=second_wordmsginletkwd_len=String.lengthkwdinletdiff=len-kwd_len-cutcolinifdiff<=0then(kwd,spec,replace_leading_tabmsg)elseletspaces=String.makediff' 'inletprefix=String.sub(replace_leading_tabmsg)0cutcolinletsuffix=String.submsgcutcol(String.lengthmsg-cutcol)in(kwd,spec,prefix^spaces^suffix)letalign?(limit=max_int)speclist=letcompleted=add_helpspeclistinletlen=List.fold_leftmax_arg_len0completedinletlen=minlenlimitinList.map(add_paddinglen)completedlettrim_crs=letlen=String.lengthsiniflen>0&&String.gets(len-1)='\r'thenString.subs0(len-1)elsesletread_auxtrimsepfile=letic=open_in_binfileinletbuf=Buffer.create200inletwords=ref[]inletstash()=letword=Buffer.contentsbufinletword=iftrimthentrim_crwordelsewordinwords:=word::!words;Buffer.clearbufinbegintrywhiletruedoletc=input_charicinifc=septhenstash()elseBuffer.add_charbufcdonewithEnd_of_file->()end;ifBuffer.lengthbuf>0thenstash();close_inic;Array.of_list(List.rev!words)letread_arg=read_auxtrue'\n'letread_arg0=read_auxfalse'\x00'letwrite_auxsepfileargs=letoc=open_out_binfileinArray.iter(funs->fprintfoc"%s%c"ssep)args;close_outocletwrite_arg=write_aux'\n'letwrite_arg0=write_aux'\x00'