12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337(* Yoann Padioleau
*
* Copyright (C) 1998-2013 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library 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 file
* license.txt for more details.
*)(*###########################################################################*)(* Prelude *)(*###########################################################################*)(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* The following functions should be in their respective sections but
* because some functions in some sections use functions in other
* sections, and because I don't want to take care of the order of
* those sections, of those dependencies, I put the functions causing
* dependency problem here. C is better than OCaml on this with the
* ability to declare prototypes, enabling some form of forward
* reference.
*)letspf=Printf.sprintfexceptionTimeoutexceptionUnixExitofintletrecdropnxs=match(n,xs)with|(0,_)->xs|(_,[])->failwith"drop: not enough"|(n,x::xs)->drop(n-1)xslettakenxs=letrecnextnxsacc=match(n,xs)with|(0,_)->List.revacc|(_,[])->failwith"Common.take: not enough"|(n,x::xs)->next(n-1)xs(x::acc)innextnxs[]letrecenum_origxn=ifx=nthen[n]elsex::enum_orig(x+1)nletenumxn=ifnot(x<=n)thenfailwith(Printf.sprintf"bad values in enum, expect %d <= %d"xn);letrecenum_auxaccxn=ifx=nthenn::accelseenum_aux(x::acc)(x+1)ninList.rev(enum_aux[]xn)letpushvl=l:=v::!lletdebugger=reffalseletunwind_protectfcleanup=if!debuggerthenf()elsetryf()withe->begincleanupe;raiseeendletfinalizefcleanup=(* bug: we can not just call f in debugger mode because
* this change the semantic of the program. I originally
* put this code below:
* if !debugger then f () else
* because I wanted some errors to pop-out to the top so I can
* debug them but because now I use save_excursion and finalize
* quite a lot this changes too much the semantic.
* TODO: maybe I should not use save_excursion so much ? maybe
* -debugger helps see code that I should refactor ?
*)tryletres=f()incleanup();reswithe->cleanup();raiseelet(unlines:stringlist->string)=funs->(String.concat"\n"s)^"\n"let(lines:string->stringlist)=funs->letreclines_aux=function|[]->[]|[x]->ifx=""then[]else[x]|x::xs->x::lines_auxxsinStr.split_delim(Str.regexp"\n")s|>lines_auxletsave_excursionreferencenewvf=letold=!referenceinreference:=newv;finalizef(fun_->reference:=old;)letmemoized?(use_cache=true)hkf=ifnotuse_cachethenf()elsetryHashtbl.findhkwithNot_found->letv=f()inbeginHashtbl.addhkv;vendexceptionTodoexceptionImpossibleexceptionMulti_found(* to be consistent with Not_found *)letexn_to_sexn=Printexc.to_stringexn(*###########################################################################*)(* Basic features *)(*###########################################################################*)(*****************************************************************************)(* Debugging/logging *)(*****************************************************************************)letprs=print_strings;print_string"\n";flushstdoutletpr2s=prerr_strings;prerr_string"\n";flushstderrletpr_xxxxxxxxxxxxxxxxx()=pr"-----------------------------------------------------------------------"letpr2_xxxxxxxxxxxxxxxxx()=pr2"-----------------------------------------------------------------------"let_already_printed=Hashtbl.create101letdisable_pr2_once=reffalseletxxx_oncefs=if!disable_pr2_oncethenpr2selseifnot(Hashtbl.mem_already_printeds)thenbeginHashtbl.add_already_printedstrue;f("(ONCE) "^s);endletpr2_onces=xxx_oncepr2s(* start of dumper.ml *)(* Dump an OCaml value into a printable string.
* By Richard W.M. Jones (rich@annexia.org).
* dumper.ml 1.2 2005/02/06 12:38:21 rich Exp
*)openPrintfopenObjletrecdump2r=ifis_intrthenstring_of_int(magicr:int)else((* Block. *)letrecget_fieldsacc=function|0->acc|n->letn=n-1inget_fields(fieldrn::acc)ninletrecis_listr=ifis_intrthen(if(magicr:int)=0thentrue(* [] *)elsefalse)else(lets=sizerandt=tagrinift=0&&s=2thenis_list(fieldr1)(* h :: t *)elsefalse)inletrecget_listr=ifis_intrthen[]elseleth=fieldr0andt=get_list(fieldr1)inh::tinletopaquename=(* XXX In future, print the address of value 'r'. Not possible in
* pure OCaml at the moment.
*)"<"^name^">"inlets=sizerandt=tagrin(* From the tag, determine the type of block. *)ifis_listrthen((* List. *)letfields=get_listrin"["^String.concat"; "(List.mapdump2fields)^"]")elseift=0then((* Tuple, array, record. *)letfields=get_fields[]sin"("^String.concat", "(List.mapdump2fields)^")")(* Note that [lazy_tag .. forward_tag] are < no_scan_tag. Not
* clear if very large constructed values could have the same
* tag. XXX *)elseift=lazy_tagthenopaque"lazy"elseift=closure_tagthenopaque"closure"elseift=object_tagthen((* Object. *)letfields=get_fields[]sinletclasz,id,slots=matchfieldswithh::h'::t->h,h',t|_->assertfalsein(* No information on decoding the class (first field). So just print
* out the ID and the slots.
*)"Object #"^dump2id^" ("^String.concat", "(List.mapdump2slots)^")")elseift=infix_tagthenopaque"infix"elseift=forward_tagthenopaque"forward"elseift<no_scan_tagthen((* Constructed value. *)letfields=get_fields[]sin"Tag"^string_of_intt^" ("^String.concat", "(List.mapdump2fields)^")")elseift=string_tagthen("\""^String.escaped(magicr:string)^"\"")elseift=double_tagthen(string_of_float(magicr:float))elseift=abstract_tagthenopaque"abstract"elseift=custom_tagthenopaque"custom"elsefailwith("dump: impossible tag ("^string_of_intt^")"))letdumpv=dump2(reprv)(* end of dumper.ml *)(*
let (dump : 'a -> string) = fun x ->
Dumper.dump x
*)letpr2_genx=pr2(dumpx)(*****************************************************************************)(* Profiling *)(*****************************************************************************)typeprof=ProfAll|ProfNone|ProfSomeofstringlistletprofile=refProfNoneletshow_trace_profile=reffalseletcheck_profilecategory=match!profilewith|ProfAll->true|ProfNone->false|ProfSomel->List.memcategoryllet_profile_table=ref(Hashtbl.create100)letadjust_profile_entrycategorydifftime=let(xtime,xcount)=(tryHashtbl.find!_profile_tablecategorywithNot_found->letxtime=ref0.0inletxcount=ref0inHashtbl.add!_profile_tablecategory(xtime,xcount);(xtime,xcount))inxtime:=!xtime+.difftime;xcount:=!xcount+1;()letprofile_startcategory=failwith"todo"letprofile_endcategory=failwith"todo"(* subtil: don't forget to give all argumens to f, otherwise partial app
* and will profile nothing.
*
* todo: try also detect when complexity augment each time, so can
* detect the situation for a function gets worse and worse ?
*)letprofile_codecategoryf=ifnot(check_profilecategory)thenf()elsebeginif!show_trace_profilethenpr2(spf"> %s"category);lett=Unix.gettimeofday()inletres,prefix=trySome(f()),""withTimeout->None,"*"inletcategory=prefix^categoryin(* add a '*' to indicate timeout func *)lett'=Unix.gettimeofday()inif!show_trace_profilethenpr2(spf"< %s"category);adjust_profile_entrycategory(t'-.t);(matchreswith|Someres->res|None->raiseTimeout);endlet_is_in_exclusif=ref(None:stringoption)letprofile_code_exclusifcategoryf=ifnot(check_profilecategory)thenf()elsebeginmatch!_is_in_exclusifwith|Somes->failwith(spf"profile_code_exclusif: %s but already in %s "categorys);|None->_is_in_exclusif:=(Somecategory);finalize(fun()->profile_codecategoryf)(fun()->_is_in_exclusif:=None)endletprofile_code_inside_exclusif_okcategoryf=failwith"Todo"let(with_open_stringbuf:(((string->unit)*Buffer.t)->unit)->string)=funf->letbuf=Buffer.create1000inletprs=Buffer.add_stringbuf(s^"\n")inf(pr,buf);Buffer.contentsbuf(* todo: also put % ? also add % to see if coherent numbers *)letprofile_diagnostic()=if!profile=ProfNonethen""elseletxs=Hashtbl.fold(funkvacc->(k,v)::acc)!_profile_table[]|>List.sort(fun(k1,(t1,n1))(k2,(t2,n2))->comparet2t1)inwith_open_stringbuf(fun(pr,_)->pr"---------------------";pr"profiling result";pr"---------------------";xs|>List.iter(fun(k,(t,n))->pr(Printf.sprintf"%-40s : %10.3f sec %10d count"k!t!n)))letreport_if_take_timetimethresholdsf=lett=Unix.gettimeofday()inletres=f()inlett'=Unix.gettimeofday()inif(t'-.t>float_of_inttimethreshold)thenpr2(Printf.sprintf"Note: processing took %7.1fs: %s"(t'-.t)s);resletprofile_code2categoryf=profile_codecategory(fun()->if!profile=ProfAllthenpr2("starting: "^category);lett=Unix.gettimeofday()inletres=f()inlett'=Unix.gettimeofday()inif!profile=ProfAllthenpr2(spf"ending: %s, %fs"category(t'-.t));res)(*****************************************************************************)(* Test *)(*****************************************************************************)(* See OUnit *)(*****************************************************************************)(* Persistence *)(*****************************************************************************)letget_valuefilename=letchan=open_infilenameinletx=input_valuechanin(* <=> Marshal.from_channel *)(close_inchan;x)letwrite_valuevalufilename=letchan=open_outfilenamein(output_valuechanvalu;(* <=> Marshal.to_channel *)(* Marshal.to_channel chan valu [Marshal.Closures]; *)close_outchan)(*****************************************************************************)(* Composition/Control *)(*****************************************************************************)(*****************************************************************************)(* Error managment *)(*****************************************************************************)(*****************************************************************************)(* Arguments/options and command line (cocci and acomment) *)(*****************************************************************************)(*
* todo? isn't unison or scott-mcpeak-lib-in-cil handles that kind of
* stuff better ? That is the need to localize command line argument
* while still being able to gathering them. Same for logging.
* Similiar to the type prof = PALL | PNONE | PSOME of string list.
* Same spirit of fine grain config in log4j ?
*
* todo? how mercurial/cvs/git manage command line options ? because they
* all have a kind of DSL around arguments with some common options,
* specific options, conventions, etc.
*
*
* todo? generate the corresponding noxxx options ?
* todo? generate list of options and show their value ?
*
* todo? make it possible to set this value via a config file ?
*
*
*)typearg_spec_full=Arg.key*Arg.spec*Arg.doctypecmdline_options=arg_spec_fulllist(* the format is a list of triples:
* (title of section * (optional) explanation of sections * options)
*)typeoptions_with_title=string*string*arg_spec_fulllisttypecmdline_sections=options_with_titlelist(* ---------------------------------------------------------------------- *)(* now I use argv as I like at the call sites to show that
* this function internally use argv.
*)letparse_optionsoptionsusage_msgargv=letargs=ref[]in(tryArg.parse_argvargvoptions(funfile->args:=file::!args)usage_msg;args:=List.rev!args;!argswith|Arg.Badmsg->Printf.eprintf"%s"msg;exit2|Arg.Helpmsg->Printf.printf"%s"msg;exit0)letusageusage_msgoptions=Arg.usage(Arg.alignoptions)usage_msg(* for coccinelle *)(* If you don't want the -help and --help that are appended by Arg.align *)letarg_align2xs=Arg.alignxs|>List.rev|>drop2|>List.revletshort_usageusage_msg~short_opt=usageusage_msgshort_optletlong_usageusage_msg~short_opt~long_opt=prusage_msg;pr"";letall_options_with_title=(("main options","",short_opt)::long_opt)inall_options_with_title|>List.iter(fun(title,explanations,xs)->prtitle;pr_xxxxxxxxxxxxxxxxx();ifexplanations<>""thenbeginprexplanations;pr""end;arg_align2xs|>List.iter(fun(key,action,s)->pr(" "^key^s));pr"";);()(* copy paste of Arg.parse. Don't want the default -help msg *)letarg_parse2lmsgshort_usage_fun=letargs=ref[]inletf=(funfile->args:=file::!args)inletl=Arg.alignlin(trybeginArg.parse_argvSys.argvlfmsg;args:=List.rev!args;!argsendwith|Arg.Badmsg->(* eprintf "%s" msg; exit 2; *)letxs=linesmsgin(* take only head, it's where the error msg is *)pr2(List.hdxs);short_usage_fun();raise(UnixExit(2))|Arg.Helpmsg->(* printf "%s" msg; exit 0; *)raiseImpossible(* -help is specified in speclist *))(* ---------------------------------------------------------------------- *)typeflag_spec=Arg.key*Arg.spec*Arg.doctypeaction_spec=Arg.key*Arg.doc*action_funcandaction_func=(stringlist->unit)typecmdline_actions=action_speclistexceptionWrongNumberOfArgumentsletoptions_of_actionsaction_refactions=actions|>List.map(fun(key,doc,_func)->(key,(Arg.Unit(fun()->action_ref:=key)),doc))let(action_list:cmdline_actions->Arg.keylist)=funxs->List.map(fun(a,b,c)->a)xslet(do_action:Arg.key->stringlist(* args *)->cmdline_actions->unit)=funkeyargsxs->letassoc=xs|>List.map(fun(a,b,c)->(a,c))inletaction_func=List.assockeyassocinaction_funcargs(* todo? if have a function with default argument ? would like a
* mk_action_0_or_1_arg ?
*)letmk_action_0_argf=(function|[]->f()|_->raiseWrongNumberOfArguments)letmk_action_1_argf=(function|[file]->ffile|_->raiseWrongNumberOfArguments)letmk_action_2_argf=(function|[file1;file2]->ffile1file2|_->raiseWrongNumberOfArguments)letmk_action_3_argf=(function|[file1;file2;file3]->ffile1file2file3|_->raiseWrongNumberOfArguments)letmk_action_4_argf=(function|[file1;file2;file3;file4]->ffile1file2file3file4|_->raiseWrongNumberOfArguments)letmk_action_n_argf=f(*****************************************************************************)(* Equality *)(*****************************************************************************)let(=|=):int->int->bool=(=)let(=<=):char->char->bool=(=)let(=$=):string->string->bool=(=)let(=:=):bool->bool->bool=(=)let(=*=)=(=)(*###########################################################################*)(* Basic types *)(*###########################################################################*)(*****************************************************************************)(* Bool *)(*****************************************************************************)(*****************************************************************************)(* Char *)(*****************************************************************************)(*****************************************************************************)(* Num *)(*****************************************************************************)(*****************************************************************************)(* Tuples *)(*****************************************************************************)(*****************************************************************************)(* Maybe *)(*****************************************************************************)(* type 'a maybe = Just of 'a | None *)let(>>=)m1m2=matchm1with|None->None|Somex->m2x(*
(*http://roscidus.com/blog/blog/2013/10/13/ocaml-tips/#handling-option-types*)
let (|?) maybe default =
match maybe with
| Some v -> v
| None -> Lazy.force default
*)letmap_optf=function|None->None|Somex->Some(fx)letdo_optionf=function|None->()|Somex->fxletopt=do_optionletopt_to_list:'aoption->'alist=function|None->[]|Somex->[x](* not sure why but can't use let (?:) a b = ... then at use time ocaml yells*)let(|||)ab=matchawith|Somex->x|None->btype('a,'b)either=Leftof'a|Rightof'b(* with sexp *)type('a,'b,'c)either3=Left3of'a|Middle3of'b|Right3of'c(* with sexp *)letpartition_eitherfl=letrecpart_eitherleftright=function|[]->(List.revleft,List.revright)|x::l->(matchfxwith|Lefte->part_either(e::left)rightl|Righte->part_eitherleft(e::right)l)inpart_either[][]lletpartition_either3fl=letrecpart_eitherleftmiddleright=function|[]->(List.revleft,List.revmiddle,List.revright)|x::l->(matchfxwith|Left3e->part_either(e::left)middlerightl|Middle3e->part_eitherleft(e::middle)rightl|Right3e->part_eitherleftmiddle(e::right)l)inpart_either[][][]lletrecfilter_some=function|[]->[]|None::l->filter_somel|Somee::l->e::filter_somelletmap_filterfxs=xs|>List.mapf|>filter_someletrecfind_some_optp=function|[]->None|x::l->matchpxwith|Somev->Somev|None->find_some_optplletfind_somepxs=matchfind_some_optpxswith|None->raiseNot_found|Somex->xletrecfind_optfxs=find_some_opt(funx->iffxthenSomexelseNone)xs(*****************************************************************************)(* Regexp, can also use PCRE *)(*****************************************************************************)let(matched:int->string->string)=funis->Str.matched_groupisletmatched1=funs->matched1sletmatched2=funs->(matched1s,matched2s)letmatched3=funs->(matched1s,matched2s,matched3s)letmatched4=funs->(matched1s,matched2s,matched3s,matched4s)letmatched5=funs->(matched1s,matched2s,matched3s,matched4s,matched5s)letmatched6=funs->(matched1s,matched2s,matched3s,matched4s,matched5s,matched6s)letmatched7=funs->(matched1s,matched2s,matched3s,matched4s,matched5s,matched6s,matched7s)let_memo_compiled_regexp=Hashtbl.create101letcandidate_match_funcsre=(* old: Str.string_match (Str.regexp re) s 0 *)letcompile_re=memoized_memo_compiled_regexpre(fun()->Str.regexpre)inStr.string_matchcompile_res0letmatch_funcsre=profile_code"Common.=~"(fun()->candidate_match_funcsre)let(=~)sre=match_funcsreletsplitseps=Str.split(Str.regexpsep)sletjoinsepxs=String.concatsepxs(*****************************************************************************)(* Strings *)(*****************************************************************************)(* ruby *)leti_to_s=string_of_intlets_to_i=int_of_stringletnull_strings=s=$=""(*****************************************************************************)(* Filenames *)(*****************************************************************************)typefilename=string(* TODO could check that exist :) type sux *)(* with sexp *)typedirname=string(* TODO could check that exist :) type sux *)(* with sexp *)(* file or dir *)typepath=stringletchop_dirsymbol=function|swhens=~"\\(.*\\)/$"->matched1s|s->s(* pre: prj_path must not contain regexp symbol *)letfilename_without_leading_pathprj_paths=letprj_path=chop_dirsymbolprj_pathinifs=$=prj_paththen"."elseifs=~("^"^prj_path^"/\\(.*\\)$")thenmatched1selsefailwith(spf"cant find filename_without_project_path: %s %s"prj_paths)letreadable~roots=ifroot="/"thenselsefilename_without_leading_pathrootsletis_directoryfile=(Unix.statfile).Unix.st_kind=*=Unix.S_DIR(*****************************************************************************)(* Dates *)(*****************************************************************************)(*****************************************************************************)(* Lines/words/strings *)(*****************************************************************************)(*****************************************************************************)(* Process/Files *)(*****************************************************************************)letcommand2s=ignore(Sys.commands)exceptionCmdErrorofUnix.process_status*stringletprocess_output_to_list2?(verbose=false)command=letchan=Unix.open_process_incommandinletres=ref([]:stringlist)inletrecprocess_otl_aux()=lete=input_linechaninres:=e::!res;ifverbosethenpr2e;process_otl_aux()intryprocess_otl_aux()withEnd_of_file->letstat=Unix.close_process_inchanin(List.rev!res,stat)letcmd_to_list?verbosecommand=let(l,exit_status)=process_output_to_list2?verbosecommandinmatchexit_statuswith|Unix.WEXITED0->l|_->raise(CmdError(exit_status,(spf"CMD = %s, RESULT = %s"command(String.concat"\n"l))))letcmd_to_list_and_status=process_output_to_list2(* tail recursive efficient version *)letcatfile=letchan=open_infileinletreccat_auxacc()=(* cant do input_line chan::aux() cos ocaml eval from right to left ! *)let(b,l)=try(true,input_linechan)withEnd_of_file->(false,"")inifbthencat_aux(l::acc)()elseaccincat_aux[]()|>List.rev|>(funx->close_inchan;x)letread_filefile=letic=open_infileinletsize=in_channel_lengthicinletbuf=Bytes.createsizeinreally_inputicbuf0size;close_inic;buf|>Bytes.to_stringletwrite_file~files=letchan=open_outfilein(output_stringchans;close_outchan)(* could be in control section too *)letfilemtimefile=(Unix.statfile).Unix.st_mtime(*
Using an external C functions complicates the linking process of
programs using commons/. Thus, I replaced realpath() with an OCaml-only
similar functions fullpath().
external c_realpath: string -> string option = "caml_realpath"
let realpath2 path =
match c_realpath path with
| Some s -> s
| None -> failwith (spf "problem with realpath on %s" path)
let realpath2 path =
let stat = Unix.stat path in
let dir, suffix =
match stat.Unix.st_kind with
| Unix.S_DIR -> path, ""
| _ -> Filename.dirname path, Filename.basename path
in
let oldpwd = Sys.getcwd () in
Sys.chdir dir;
let realpath_dir = Sys.getcwd () in
Sys.chdir oldpwd;
Filename.concat realpath_dir suffix
let realpath path =
profile_code "Common.realpath" (fun () -> realpath2 path)
*)letfullpathfile=ifnot(Sys.file_existsfile)thenfailwith(spf"fullpath: file (or directory) %s does not exist"file);letdir,base=ifSys.is_directoryfilethenfile,NoneelseFilename.dirnamefile,Some(Filename.basenamefile)in(* save *)letold=Sys.getcwd()inSys.chdirdir;lethere=Sys.getcwd()in(* restore *)Sys.chdirold;matchbasewith|None->here|Somex->Filename.concatherex(* Why a use_cache argument ? because sometimes want disable it but dont
* want put the cache_computation funcall in comment, so just easier to
* pass this extra option.
*)letcache_computation2?(verbose=false)?(use_cache=true)fileext_cachef=ifnotuse_cachethenf()elsebeginifnot(Sys.file_existsfile)thenbeginpr2("WARNING: cache_computation: can't find file "^file);pr2("defaulting to calling the function");f()endelsebeginletfile_cache=(file^ext_cache)inifSys.file_existsfile_cache&&filemtimefile_cache>=filemtimefilethenbeginifverbosethenpr2("using cache: "^file_cache);get_valuefile_cacheendelsebeginletres=f()inwrite_valueresfile_cache;resendendendletcache_computation?verbose?use_cacheabc=profile_code"Common.cache_computation"(fun()->cache_computation2?verbose?use_cacheabc)(* emacs/lisp inspiration (eric cooper and yaron minsky use that too) *)let(with_open_outfile:filename->(((string->unit)*out_channel)->'a)->'a)=funfilef->letchan=open_outfileinletprs=output_stringchansinunwind_protect(fun()->letres=f(pr,chan)inclose_outchan;res)(fune->close_outchan)let(with_open_infile:filename->((in_channel)->'a)->'a)=funfilef->letchan=open_infileinunwind_protect(fun()->letres=fchaninclose_inchan;res)(fune->close_inchan)(* now in prelude:
* exception Timeout
*)(* it seems that the toplevel block such signals, even with this explicit
* command :(
* let _ = Unix.sigprocmask Unix.SIG_UNBLOCK [Sys.sigalrm]
*)(* could be in Control section *)(* subtil: have to make sure that timeout is not intercepted before here, so
* avoid exn handle such as try (...) with _ -> cos timeout will not bubble up
* enough. In such case, add a case before such as
* with Timeout -> raise Timeout | _ -> ...
*
* question: can we have a signal and so exn when in a exn handler ?
*)lettimeout_function?(verbose=false)timeoutval=funf->trybeginSys.set_signalSys.sigalrm(Sys.Signal_handle(fun_->raiseTimeout));ignore(Unix.alarmtimeoutval);letx=f()inignore(Unix.alarm0);xendwithTimeout->beginifverbosethenpr2"timeout (we abort)";raiseTimeout;end|e->(* subtil: important to disable the alarm before relaunching the exn,
* otherwise the alarm is still running.
*
* robust?: and if alarm launched after the log (...) ?
* Maybe signals are disabled when process an exception handler ?
*)beginignore(Unix.alarm0);(* log ("exn while in transaction (we abort too, even if ...) = " ^
Printexc.to_string e);
*)ifverbosethenpr2"exn while in timeout_function";raiseeend(* creation of tmp files, a la gcc *)let_temp_files_created=ref([]:filenamelist)(* ex: new_temp_file "cocci" ".c" will give "/tmp/cocci-3252-434465.c" *)letnew_temp_fileprefixsuffix=letprocessid=i_to_s(Unix.getpid())inlettmp_file=Filename.temp_file(prefix^"-"^processid^"-")suffixinpushtmp_file_temp_files_created;tmp_fileletsave_tmp_files=reffalseleterase_temp_files()=ifnot!save_tmp_filesthenbegin!_temp_files_created|>List.iter(funs->(* pr2 ("erasing: " ^ s); *)command2("rm -f "^s));_temp_files_created:=[]endleterase_this_temp_filef=ifnot!save_tmp_filesthenbegin_temp_files_created:=List.filter(functionx->not(x=$=f))!_temp_files_created;command2("rm -f "^f)end(*###########################################################################*)(* Collection-like types *)(*###########################################################################*)(*****************************************************************************)(* List *)(*****************************************************************************)letexcludepxs=List.filter(funx->not(px))xsletrec(span:('a->bool)->'alist->'alist*'alist)=funp->function|[]->([],[])|x::xs->ifpxthenlet(l1,l2)=spanpxsin(x::l1,l2)else([],x::xs)letrectake_safenxs=match(n,xs)with|(0,_)->[]|(_,[])->[]|(n,x::xs)->x::take_safe(n-1)xsletgroup_byfxs=(* use Hashtbl.find_all property *)leth=Hashtbl.create101in(* could use Set *)lethkeys=Hashtbl.create101inxs|>List.iter(funx->letk=fxinHashtbl.replacehkeysktrue;Hashtbl.addhkx);Hashtbl.fold(funk_acc->(k,Hashtbl.find_allhk)::acc)hkeys[]letgroup_by_multifkeysxs=(* use Hashtbl.find_all property *)leth=Hashtbl.create101in(* could use Set *)lethkeys=Hashtbl.create101inxs|>List.iter(funx->letks=fkeysxinks|>List.iter(funk->Hashtbl.replacehkeysktrue;Hashtbl.addhkx;));Hashtbl.fold(funk_acc->(k,Hashtbl.find_allhk)::acc)hkeys[](* you should really use group_assoc_bykey_eff *)letrecgroup_by_mapped_keyfkeyl=matchlwith|[]->[]|x::xs->letk=fkeyxinlet(xs1,xs2)=List.partition(funx'->letk2=fkeyx'ink=*=k2)xsin(k,(x::xs1))::(group_by_mapped_keyfkeyxs2)letreczipxsys=match(xs,ys)with|([],[])->[]|([],_)->failwith"zip: not same length"|(_,[])->failwith"zip: not same length"|(x::xs,y::ys)->(x,y)::zipxsysletnullxs=matchxswith[]->true|_->falseletindex_listxs=ifnullxsthen[](* enum 0 (-1) generate an exception *)elsezipxs(enum0((List.lengthxs)-1))letindex_list_0xs=index_listxsletindex_list_1xs=xs|>index_list|>List.map(fun(x,i)->x,i+1)letsort_profab=profile_code"Common.sort_by_xxx"(fun()->List.sortab)typeorder=HighFirst|LowFirstletcompare_orderorderab=matchorderwith|HighFirst->compareba|LowFirst->compareabletsort_by_val_highfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparev2v1)xsletsort_by_val_lowfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparev1v2)xsletsort_by_key_highfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparek2k1)xsletsort_by_key_lowfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparek1k2)xs(*****************************************************************************)(* Assoc *)(*****************************************************************************)type('a,'b)assoc=('a*'b)list(*****************************************************************************)(* Arrays *)(*****************************************************************************)(*****************************************************************************)(* Matrix *)(*****************************************************************************)(*****************************************************************************)(* Set. Have a look too at set*.mli *)(*****************************************************************************)(*****************************************************************************)(* Hash *)(*****************************************************************************)lethash_to_listh=Hashtbl.fold(funkvacc->(k,v)::acc)h[]|>List.sortcomparelethash_of_listxs=leth=Hashtbl.create101inxs|>List.iter(fun(k,v)->Hashtbl.replacehkv);h(*****************************************************************************)(* Hash sets *)(*****************************************************************************)type'ahashset=('a,bool)Hashtbl.t(* with sexp *)lethashset_to_listh=hash_to_listh|>List.mapfst(* old: slightly slower?
* let hashset_of_list xs =
* xs +> List.map (fun x -> x, true) +> hash_of_list
*)lethashset_of_list(xs:'alist):('a,bool)Hashtbl.t=leth=Hashtbl.create(List.lengthxs)inxs|>List.iter(funk->Hashtbl.replacehktrue);hlethkeysh=lethkey=Hashtbl.create101inh|>Hashtbl.iter(funkv->Hashtbl.replacehkeyktrue);hashset_to_listhkeyletgroup_assoc_bykey_eff2xs=leth=Hashtbl.create101inxs|>List.iter(fun(k,v)->Hashtbl.addhkv);letkeys=hkeyshinkeys|>List.map(funk->k,Hashtbl.find_allhk)letgroup_assoc_bykey_effxs=profile_code"Common.group_assoc_bykey_eff"(fun()->group_assoc_bykey_eff2xs)(*****************************************************************************)(* Stack *)(*****************************************************************************)type'astack='alist(* with sexp *)(*****************************************************************************)(* Tree *)(*****************************************************************************)(*****************************************************************************)(* Graph. Have a look too at Ograph_*.mli *)(*****************************************************************************)(*****************************************************************************)(* Generic op *)(*****************************************************************************)letsortxs=List.sortcomparexs(*###########################################################################*)(* Misc functions *)(*###########################################################################*)(*###########################################################################*)(* Postlude *)(*###########################################################################*)(*****************************************************************************)(* Flags and actions *)(*****************************************************************************)(*****************************************************************************)(* Postlude *)(*****************************************************************************)(*****************************************************************************)(* Misc *)(*****************************************************************************)(* now in prelude: exception UnixExit of int *)letexn_to_real_unixexitf=tryf()withUnixExitx->exitxletpp_do_in_zero_boxf=Format.open_box0;f();Format.close_box()letmain_boilerplatef=ifnot(!Sys.interactive)thenexn_to_real_unixexit(fun()->Sys.set_signalSys.sigint(Sys.Signal_handle(fun_->pr2"C-c intercepted, will do some cleaning before exiting";(* But if do some try ... with e -> and if do not reraise the exn,
* the bubble never goes at top and so I cant really C-c.
*
* A solution would be to not raise, but do the erase_temp_file in the
* syshandler, here, and then exit.
* The current solution is to not do some wild try ... with e
* by having in the exn handler a case: UnixExit x -> raise ... | e ->
*)Sys.set_signalSys.sigintSys.Signal_default;raise(UnixExit(-1))));(* The finalize below makes it tedious to go back to exn when use
* 'back' in the debugger. Hence this special case. But the
* Common.debugger will be set in main(), so too late, so
* have to be quicker
*)ifSys.argv|>Array.to_list|>List.exists(funx->x=$="-debugger")thendebugger:=true;finalize(fun()->pp_do_in_zero_box(fun()->tryf();(* <---- here it is *)withUnix.Unix_error(e,fm,argm)->pr2(spf"exn Unix_error: %s %s %s\n"(Unix.error_messagee)fmargm);raise(Unix.Unix_error(e,fm,argm))))(fun()->if!profile<>ProfNonethenbeginpr2(profile_diagnostic());Gc.print_statstderr;end;erase_temp_files();))(* let _ = if not !Sys.interactive then (main ()) *)letfollow_symlinks=reffalseletarg_symlink()=if!follow_symlinksthen" -L "else""letgrep_dash_v_str="| grep -v /.hg/ |grep -v /CVS/ | grep -v /.git/ |grep -v /_darcs/"^"| grep -v /.svn/ | grep -v .git_annot | grep -v .marshall"letfiles_of_dir_or_files_no_vcs_nofilterxs=xs|>List.map(funx->ifis_directoryxthen(* todo: should escape x *)letcmd=(spf"find %s '%s' -type f %s"(* -noleaf *)(arg_symlink())xgrep_dash_v_str)inlet(xs,status)=cmd_to_list_and_statuscmdin(matchstatuswith|Unix.WEXITED0->xs|_->raise(CmdError(status,(spf"CMD = %s, RESULT = %s"cmd(String.concat"\n"xs)))))else[x])|>List.concat(*****************************************************************************)(* Maps *)(*****************************************************************************)moduleSMap=Map.Make(String)type'asmap='aSMap.t