123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220(* Main entry point of the OCamlRes library. *)(* This file is part of ocp-ocamlres - main library
* (C) 2013 OCamlPro - Benjamin CANOU
*
* ocp-ocamlres 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.0 of the License, or (at your option) any later
* version, with linking exception.
*
* ocp-ocamlres 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 LICENSE file for more details *)modulePath=structtypet=dirs*nameoptionanddirs=stringlistandname=string*extoptionandext=stringletsplit_extpstr=letlen=String.lengthpstrinletrecloopcurlast=ifcur<0||pstr.[cur]='/'thencutlastelseifpstr.[cur]='.'thenloop(cur-1)(cur-1)elseloop(cur-1)lastandcutpos=ifpos=len-1then(pstr,None)else(String.subpstr0(pos+1),Some(String.subpstr(pos+2)(len-pos-2)))inloop(len-1)(len-1)letsplit_basepstr=letlen=String.lengthpstrinletrecloopcur=ifcur<0then("",Somepstr)elseifpstr.[cur]='/'thenifcur=len-1then(pstr,None)else(String.subpstr0cur,Some(String.subpstr(cur+1)(len-cur-1)))elseloop(cur-1)inloop(len-1)letsplit_dirspstr=letlen=String.lengthpstrinletrecloopacccurlast=ifcur<0||pstr.[cur]='/'thencutacccurlastelseloopacc(cur-1)lastandcutaccposlast=letacc=ifpos=lastthenaccelseString.subpstr(pos+1)(last-pos)::accinifpos<0thenaccelseloopacc(pos-1)(pos-1)inloop[](len-1)(len-1)letshorten(dirs,file)=letrecloopaccdirs=matchacc,dirswith|[],".."::tl->loop[]tl|_::pacc,".."::tl->looppacctl|_,"."::tl->loopacctl|_,d::tl->loop(d::acc)tl|_,[]->List.revaccinloop[]dirs,fileletname_of_stringpstr=ifString.lengthpstr=0theninvalid_arg"OCamlRes.Path.name_of_string";split_extpstrletstring_of_name(name,ext)=matchextwith|None->name|Someext->name^"."^extletof_stringpstr=ifString.lengthpstr=0theninvalid_arg"OCamlRes.Path.of_string";letdirs,base=split_basepstrinletpath=matchbasewith|None->split_dirsdirs,None|Some("."|"..")->split_dirspstr,None|Somebase->split_dirsdirs,(Some(split_extbase))inshortenpathletto_string(dirs,file)=letopenBufferinletbuf=create255inList.iter(funp->add_charbuf'/';add_stringbufp)dirs;(matchfilewith|None->()|Some(b,ext)->add_charbuf'/';add_stringbufb;matchextwith|None->()|Somee->add_charbuf'.';add_stringbufe);Buffer.contentsbufendmoduleRes=structtype'anode=|Dirofstring*'anodelist|Fileofstring*'a|Errorofstringtype'aroot='anodelistmoduleSM=Map.Make(String)moduleSS=Set.Make(String)letrecmap_nodef=function|Dir(n,l)->Dir(n,mapfl)|File(n,v)->File(n,fv)|Errore->Erroreandmapfl=List.map(map_nodef)lletrecmerge_nodesnode1node2=matchnode1,node2with|Dir(n1,l1),Dir(n2,l2)->ifn1<>n2then[node1;node2]else[Dir(n1,mergel1l2)]|(File(n,_)asf),(Dir(nd,_)asdir)|(Dir(nd,_)asdir),(File(n,_)asf)->ifn<>ndthen[f;dir]else[Error("unmergeable versions of "^n)]|(File(n1,c1)asf1),(File(n2,c2)asf2)->ifn1<>n2||c1=c2then[f1;f2]else[Error("unmergeable versions of "^n1)]|(Error_ase),n|n,(Error_ase)->[e;n]andmerge(rl:'aroot)(rr:'aroot):'aroot=letfiles=refSM.emptyinleterrors=refSS.emptyinletdo_one=List.iter(funnode->letto_add=matchnodewith|Dir(n,_)|File(n,_)asf->(trymerge_nodesf(SM.findn!files)withNot_found->[f])|Error_ase->[e]inList.iter(function|Errormsg->errors:=SS.addmsg!errors|Dir(n,_)|File(n,_)asf->files:=SM.addnf!files)to_add)indo_onerl;do_onerr;snd(List.split(SM.bindings!files))@List.map(funmsg->Errormsg)(SS.elements!errors)letrecfind(path:Path.t)(root:'aroot):'a=matchroot,pathwith|File(name,data)::ns,([d],None)->(* let's be flexible *)ifname=dthendataelsefindpathns|File(name,data)::ns,([],Somen)->ifname=Path.string_of_namenthendataelsefindpathns|Dir(name,ns)::ps,(d::ds,f)->ifname=dthenfind(ds,f)nselsefindpathps|(Error_|Dir_|File_)::ps,(_,Somen)->findpathps|_,_->raiseNot_foundletrecfind_dir(path:Path.t)(root:'aroot):'aroot=matchroot,pathwith|_,([],None)->root|[],_->raiseNot_found|Dir(name,ns)::ps,(d::ds,f)->ifname=dthenfind_dir(ds,f)nselsefind_dirpathps|Dir(name,ns)::ps,([],Somef)->(* let's be flexible *)ifname=Path.string_of_namefthennselsefind_dirpathps|(Error_|File_)::ps,_->find_dirpathpsletrecadd(path:Path.t)(data:'a)(root:'aroot):'aroot=matchroot,pathwith|[],([],None)->raise(Invalid_argument"OCamlRes.Res.add")|[],([],Somen)->[File(Path.string_of_namen,data)]|[],(d::ds,f)->[Dir(d,add(ds,f)data[])]|(Dir(n,_)|File(n,_))::_,([d],None)whenn=d->raise(Failure"OCamlRes.Res.add: already exists")|(Dir(n,_)|File(n,_))::_,([],Somef)whenn=(Path.string_of_namef)->raise(Failure"OCamlRes.Res.add: already exists")|Dir(name,ns)asdir::ps,(d::ds,f)->ifname=dthen[Dir(name,add(ds,f)datans)]elsedir::addpathdataps|first::ps,_->first::addpathdatapsletrecadd_prefixpathnode=matchpathwith|[]->node|dir::path->Dir(dir,[add_prefixpathnode])end