123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310(***********************************************************************)(* *)(* OCamlSpotter *)(* *)(* Jun FURUSE *)(* *)(* Copyright 2008-2012 Jun Furuse. All rights reserved. *)(* This file is distributed under the terms of the GNU Library *)(* General Public License, with the special exception on linking *)(* described in file LICENSE. *)(* *)(***********************************************************************)(* File path normalization *)moduleFilename=CopiedfilenamemoduletypeFilename=sigvalcurrent_dir_name:stringvalparent_dir_name:stringvaldir_sep:stringvalis_dir_sep:string->int->boolvalis_relative:string->boolvalis_implicit:string->boolvalcheck_suffix:string->string->boolvaltemp_dir_name:stringvalquote:string->stringvalbasename:string->stringvaldirname:string->stringvalhas_drive:string->boolvaldrive_and_path:string->string*stringvalnormalize_drive:string->stringvalis_network_drive:string->boolendmoduleUnix:Filename=structincludeFilename.Unixlethas_drive_=falseletdrive_and_paths="",sletnormalize_drives=sletis_network_drive_=falseendmoduleWin32:Filename=structincludeFilename.Win32(* We think network drives too *)(* CR jfuruse: it returns true even for "///" *)lethas_drives=has_drives||matchXstring.sub's02with|"//"|"\\\\"->true|_->falseletdrive_and_paths=matchdrive_and_pathswith|"",s->beginmatchXstring.sub's02with|("//"|"\\\\"asp)->p,String.subs2(String.lengths-2)|_->"",send|res->resletnormalize_drives=Xstring.replace_chars'/''\\'(String.uppercase_asciis)letis_network_drive=function|"//"|"\\\\"->true|_->falseendmoduleCygwin:Filename=structincludeFilename.Cygwinlethas_drive=Win32.has_driveletdrive_and_path=Win32.drive_and_pathletnormalize_drives=Xstring.replace_chars'\\''/'(String.lowercase_asciis)letis_network_drive=Win32.is_network_driveendmoduleMake(F:Filename)=structclassc=objectmethodcurrent=F.current_dir_namemethodparent=F.parent_dir_namemethodsep=F.dir_sepmethodis_relative=F.is_relativemethodis_absolutex=not(F.is_relativex)methodcheck_suffix=F.check_suffixmethoddir_and_bases=F.dirnames,F.basenamesmethodtemp_dir=F.temp_dir_namemethodquote=F.quotemethoddrive_and_path=F.drive_and_pathmethodnormalize_drive=F.normalize_drivemethodis_network_drive=F.is_network_drivemethodis_dir_sep=F.is_dir_sependendmoduleMakeUnix=Make(Unix)typeop=MakeUnix.cletunix=letmoduleM=Make(Unix)innewM.cletwin32=letmoduleM=Make(Win32)innewM.cletcygwin=letmoduleM=Make(Cygwin)innewM.ctypeos=|Unix(** We love *)|Win32(** We hate *)|Cygwin(** a failed effort of reconcillation *)letof_os=function|Unix->unix|Win32->win32|Cygwin->cygwinletos=matchSys.os_typewith|"Unix"->Unix|"Win32"->Win32|"Cygwin"->Cygwin|_->assertfalsetypet={os:os;op:op;drive:stringoption;(** Some "C:", Some "\\\\" or Some "//" *)abs:bool;revs:stringlist;(** reversed directory components: a/b/c has ["a"; "b"; "c"] *)normalized:bool;}letof_stringoss=letf=of_ososinletdrive,p=f#drive_and_pathsinletdrive=ifdrive=""thenNoneelseSomedriveinletabs=matchdrivewith|None->f#is_absolutep|Somedwhenf#is_network_drived->true|_->f#is_absolutepinletrecsplitssts=letd,b=f#dir_and_basesiniff#is_dir_sepd0&&f#is_dir_sepb0then(* In Unix at least, it means [s] is ["/"] or ["////"]. *)stelseifs=dthens::stelsesplits(b::st)dinletrevs=List.rev(splits[]p)in{os;op=f;drive;abs;revs;normalized=false}letnormalizet=ift.normalizedthentelseletf=t.opinletdrive=matcht.drivewith|None->None|Somed->Some(f#normalize_drived)in(* xxx/./yyy => xxx/yyy
xxx/a/../yyy => xxx/yyy
/../../ => /../../
*)letrecnormalize_rev=function|[]->[]|x::xswhenx=f#current->normalize_revxs|x::xswhenx=f#parent->letys=normalize_revxsinbeginmatchyswith|[]whent.abs->[](* /.. => / *)|[]->[x](* .. => .. *)|z::_whenz=f#parent->x::ys(* xxx/../.. => xxx/../.. *)|_::zs->zs(* xxx/z/.. => xxx *)end|x::xs->x::normalize_revxsinletrevs=normalize_revt.revsin{twithdrive;revs;normalized=true}letto_stringt=letcompos=List.revt.revsinletconcats=String.concatt.op#sepinmatcht.drive,t.abs,composwith|None,true,[]->t.op#sep|None,true,_->concats(""::compos)|None,false,[]->t.op#current|None,false,_->concatscompos|Somed,true,_whent.op#is_network_drived->d^concatscompos|Somed,false,_whent.op#is_network_drived->assertfalse|Somed,true,[]->d^t.op#sep|Somed,true,_->d^concats(""::compos)|Somed,false,[]->d^t.op#current|Somed,false,_->d^concatscomposletis_absolutet=t.absletis_relativet=nott.absletis_roott=t.abs&&lett=normalizetint.revs=[]letdirbaset=lett=normalizetinmatcht.revswith|[]->t,None|x::_whenx=t.op#parent->invalid_arg"dirbase"|x::xs->{twithrevs=xs},Somexlet(^/)xs=lety=of_stringx.ossinifis_absoluteytheninvalid_arg"(^/)"elsenormalize{xwithrevs=y.revs@x.revs;normalized=false}letconcatsxss=List.fold_left(^/)xssletparentt=lett=normalizetinmatcht.revswith|[]whent.abs->t|[]->{twithrevs=[t.op#parent]}|x::_whenx=t.op#parent->{twithrevs=t.op#parent::t.revs}|_::xs->{twithrevs=xs}letwraposfs=to_string(f(normalize(of_stringoss)))letis_prefixxy=ifx.os=y.os&&x.abs=y.absthenletrecis_prefixxsys=matchxs,yswith|[],ys->Someys|x::xs,y::yswhenx=y->is_prefixxsys|_->Noneinis_prefix(List.revx.revs)(List.revy.revs)elseNonelettest()=letnormosseq=letres=wrapos(funx->x)sinifres<>eqthenbeginFormat.eprintf"Filepath.test failed: %S => %S => %S@."sreseq;assertfalseendinList.iter(fun(os,s,eq)->normosseq)[Unix,"/a/b/c","/a/b/c";Unix,"a/b/c","a/b/c";Unix,"//a/b/c","/a/b/c";Unix,"///a/b/c","/a/b/c";Unix,"/","/";Unix,"//","/";Unix,"///","/";Unix,".",".";Unix,"./",".";Unix,"/.","/";Unix,"/a/./b/./c/","/a/b/c";Unix,"/a/../b/../c/","/c";Unix,"../../a/../b","../../b";Unix,"..","..";Unix,"/..","/";Unix,"a/.","a";Unix,"a//b/.","a/b";Unix,"",".";(* ??? *)Win32,"\\a\\b\\c","\\a\\b\\c";Win32,"c:\\a\\b\\c","C:\\a\\b\\c";Win32,"c:/a/b/c","C:\\a\\b\\c";Win32,"c:a/b/c","C:a\\b\\c";Win32,"c:","C:.";Win32,"//a/b","\\\\a\\b";](*
let get_component : string -> string = Hashtbl.memoize (Hashtbl.create 1023) (fun x -> x)
let dotdot = get_component (parent_dir_name)
let hashcons_list =
let cache = Hashtbl.create 1023 in
let rec f xs = Hashtbl.memoize cache (function
| [] -> []
| x::xs -> x :: f xs) xs
in
f
*)