Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file filename_extended.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206openCoreopenFilenameopenPoly(** Path *)letexplodepath=letrecaux=function|""|"."->[]|"/"->["/"]|path->letdirname,basename=splitpathinbasename::auxdirnameinList.rev(auxpath)letimplode=function|[]->"."|"/"::rest->"/"^(String.concat~sep:"/"rest)|l->String.concat~sep:"/"l(* Takes out all "../" and "./" in a path, except that if it's a relative path it may
start with some "../../" stuff at the front. *)letnormalize_pathp=List.foldp~init:[]~f:(funaccpath_element->matchpath_element,accwith(* parent of root is root, and root can only appear as first part of path *)|"..",["/"]->["/"](* just pop the stack, e.g. /foo/bar/../ becomes just /foo/ *)|"..",h::restwhenh<>".."->rest|".",v->v|_->path_element::acc(* accumulate regular dirs or chains of ... at the beginning of a
relative path*))|>List.revletmake_relative?to_f=ifto_=None&&is_relativefthenfelseletto_=matchto_with|Somedir->ifis_relativef<>is_relativedirthenfailwithf"make_relative ~to_:%s %s: cannot work on an absolute path and a \
relative one"dirf();dir|None->Sys.getcwd()inletrecaux=function|(h::t),(h'::t')whenString.equalhh'->aux(t,t')|".."::_,_->failwithf"make_relative ~to_:%s %s: negative lookahead (ie goes \"above\" the current directory)"to_f()|p,p'->(List.map~f:(fun_->parent_dir_name)p)@p'inletto_=normalize_path(explodeto_)andf=normalize_path(explodef)inimplode(aux(to_,f))let%test_module"make_relative"=(modulestructletmake_relative~to_f=trySome(make_relative~to_f)withFailure_->Nonelet%test_=make_relative~to_:"..""a"=Nonelet%test_=make_relative~to_:"..""../a"=Some"a"let%test_=make_relative~to_:"c""a/b"=Some"../a/b"let%test_=make_relative~to_:"/""a/b"=Noneend)letnormalizep=implode(normalize_path(explodep))let%test_module"normalize"=(modulestructlet%test"id"=normalize"/mnt/local"="/mnt/local"let%test"dot_dotdot"=normalize"/mnt/./../local"="/local"let%test_=normalize"/mnt/local/../global/foo"="/mnt/global/foo"let%test"beyond_root"=normalize"/mnt/local/../../.."="/"let%test"negative_lookahead"=normalize"../a/../../b"="../../b"end)let(//)srcp=ifis_absolutepthenpelseconcatsrcpletmake_absolutep=Sys.getcwd()//pletuser_homeusername=matchUnix.Passwd.getbynameusernamewith|Someuser->letpw_dir=user.Unix.Passwd.dirinifString.lengthpw_dir=0thenfailwithf"user's \"%s\"'s home is an empty string"username()elsepw_dir|None->failwithf"user \"%s\" not found"username()letexpand_users=letexpand_home=function|"~"->user_home(Shell_internal.whoami())|s->user_home(String.chop_prefix_exns~prefix:"~")inif(String.is_prefix~prefix:"~"s)thenmatchString.lsplit2~on:'/'swith|Some(base,rest)->expand_homebase^"/"^rest|None->expand_homeselsesletexpand?(from=".")p=normalize(Sys.getcwd()//from//expand_userp)letrecis_parent_pathp1p2=matchp1,p2with|["/"],_->true|((h1::p1)asl),(h2::p2)->(h1=h2&&is_parent_pathp1p2)||(h2<>".."&&h2<>"/"&&List.for_alll~f:((=)parent_dir_name))|l,[]->List.for_alll~f:((=)parent_dir_name)|[],(h::_)->h<>".."&&h<>"/"letis_parentf1f2=is_parent_path(normalize_path(explodef1))(normalize_path(explodef2))(** Filename comparison *)(*
Extension comparison:
We have a list of lists of extension that should appear consecutive to one
another. Our comparison function works by mapping extensions to
(extension*int) couples, for instance "c" is mapped to "h,1" meaning it
should come right after h.
*)letcreate_extension_mapl=List.foldl~f:(funinitl->matchlwith|[]->init|idx::_->List.foldil~f:(funposmapv->ifCore.Map.memmapvthenfailwithf"Extension %s is defined twice"v();Core.Map.setmap~key:v~data:(idx,pos))~init)~init:Map.emptyletextension_cmpmaph1h2=letlookupe=Option.value(Map.findmape)~default:(e,0)inTuple2.compare(lookuph1)(lookuph2)~cmp1:(String_extended.collate)~cmp2:(Int.compare)letbasename_comparemapf1f2=letext_splits=Option.value(String.lsplit2~on:'.'s)~default:(s,"")inTuple2.compare(ext_splitf1)(ext_splitf2)~cmp1:(String_extended.collate)~cmp2:(extension_cmpmap)letfilename_comparemapv1v2=letv1=explodev1andv2=explodev2inList.compare(basename_comparemap)v1v2letparentp=normalize(concatpparent_dir_name)let%test_module"parent"=(modulestructlet%test_=parent"/mnt/local"="/mnt"let%test_=parent"/mnt/local/../global/foo"="/mnt/global"let%test_=parent"/mnt/local/../../global"="/"end)letextension_map=create_extension_map[["h";"c"];["mli";"ml"]]letcompare=filename_compareextension_mapletwith_open_temp_file?in_dir?(write=ignore)~fprefixsuffix=protectx(open_temp_file?in_dirprefixsuffix)~f:(fun(fname,oc)->protectxoc~f:write~finally:Out_channel.close;ffname)~finally:(fun(fname,_)->Unix.unlinkfname)letwith_temp_dir?in_dirprefixsuffix~f=protectx(temp_dir?in_dirprefixsuffix)~f~finally:(fundirname->ignore(Sys.command(sprintf"rm -rf '%s'"dirname)))