123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as published *)(* by the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** Command-line options.
Replacement for [Arg] from the standard library
*)typespec=|Unitof(unit->unit)|Unit_delayedof(unit->unit)(** [Unit_delayed] functions are only executed after parsing all
the arguments, in the order they appear on the command-line.
*)|Unit_exitof(unit->unit)(** As [Unit_delayed], but exit immediately after parsing the
command-line and executing all the functions (including
[Unit_exit] and [Unit_delayed] options).
Useful for [-help].
*)|Boolof(bool->unit)|Setofboolref|Clearofboolref|Intof(int->unit)|Set_intofintref|Stringof(string->unit)|Set_stringofstringref|String_listof(stringlist->unit)|Set_string_listofstringlistref|Symbolofstringlist*(string->unit)|Symbol_delayedofstringlist*(string->unit)|Symbol_exitofstringlist*(string->unit)typearg={key:string;doc:string;category:string;default:string;spec:spec;}(** Replacement for [Arg.parse].
Adds delayed Unit arguments.
*)letparse(argv:stringarray)(args:arglist)(handler:string->unit)(rest:stringlist->unit)(msg:string)(help:unit->unit):unit=(* separate arg into program name and actual command-line arguments *)letprogname,opts=ifArray.lengthargv<1then"?",[]elseargv.(0),List.tl(Array.to_listargv)in(* Unit_delayed actions are delayed into everything is parsed *)letdelayed=ref[]in(* Should we exit at the end of parse? *)letexit_after=reffalsein(* utilities *)letto_boolav=trybool_of_stringvwith_->Printf.eprintf"%s: option %s requires a boolean argument (true or false)\n"prognamea;help();exit2andto_intav=tryint_of_stringvwith_->Printf.eprintf"%s: option %s requires an integer argument\n"prognamea;help();exit2in(* eat argument list *)letreceat=function|[]->()|a::tl->ifa=""theneattlelseifa="--"thenresttlelseifa.[0]!='-'then(handlera;eattl)else((* cut option at '=' if necessary *)letopt,arg=ifString.containsa'='thenleti=String.indexa'='inString.suba0i,Some(String.suba(i+1)(String.lengtha-i-1))elsea,Nonein(* get option argument, either after '=' or in the next
command-line argument *)letget_arg()=matcharg,tlwith|Somex,tl->x,tl|None,x::tl->x,tl|None,[]->Printf.eprintf"%s: option %s requires an argument\n"prognameopt;help();exit2andnoarg()=ifarg<>Nonethen(Printf.eprintf"%s: option %s has no argument\n"prognameopt;help();exit2)andget_arg_list()=matcharg,tlwith|Somex,tl->String.split_on_char','x,tl|None,x::tl->String.split_on_char','x,tl|None,[]->Printf.eprintf"%s: option %s requires an argument\n"prognameopt;help();exit2inifList.exists(funx->x.key=opt)argsthen(letarg=List.find(funx->x.key=opt)argsinmatcharg.specwith|Unit_delayedf->noarg();delayed:=(!delayed)@[f];eattl|Unit_exitf->exit_after:=true;noarg();delayed:=(!delayed)@[f];eattl|Unitf->noarg();f();eattl|Setr->noarg();r:=true;eattl|Clearr->noarg();r:=false;eattl|Boolf->letv,tl=get_arg()inf(to_boolav);eattl|Intf->letv,tl=get_arg()inf(to_intav);eattl|Set_intf->letv,tl=get_arg()inf:=to_intav;eattl|Stringf->letv,tl=get_arg()infv;eattl|Set_stringf->letv,tl=get_arg()inf:=v;eattl|String_listf->letv,tl=get_arg_list()infv;eattl|Set_string_listf->letv,tl=get_arg_list()inf:=(!f)@v;eattl|Symbol(l,f)->letv,tl=get_arg()inifnot(List.memvl)then(Format.eprintf"%s: option %s requires an argument in the list: [%a]\n"prognameaFormat.(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt", ")pp_print_string)l;help();exit2);fv;eattl|Symbol_delayed(l,f)->letv,tl=get_arg()inifnot(List.memvl)then(Format.eprintf"%s: option %s requires an argument in the list: [%a]\n"prognameaFormat.(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt", ")pp_print_string)l;help();exit2);delayed:=(!delayed)@[(fun()->fv)];eattl|Symbol_exit(l,f)->exit_after:=true;letv,tl=get_arg()inifnot(List.memvl)then(Format.eprintf"%s: option %s requires an argument in the list: [%a]\n"prognameaFormat.(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt", ")pp_print_string)l;help();exit2);delayed:=(!delayed)@[(fun()->fv)];eattl)else(Printf.eprintf"%s: unknown option %s\n"prognamea;help();exit2))ineatopts;(* now execute all delayed actions *)List.iter(funf->f())!delayed;if!exit_afterthenexit0