-- Copyright (C) 2002-2003 David Roundy---- This program is free software; you can redistribute it and/or modify-- it under the terms of the GNU General Public License as published by-- the Free Software Foundation; either version 2, 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 General Public License for more details.---- You should have received a copy of the GNU General Public License-- along with this program; see the file COPYING. If not, write to-- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,-- Boston, MA 02110-1301, USA.-- | FileName is an abstract type intended to facilitate the input and output of-- unicode filenames.moduleDarcs.Patch.FileName(FileName(),fp2fn,fn2fp,fn2ps,ps2fn,niceps2fn,fn2niceps,breakOnDir,normPath,ownName,superName,movedirfilename,encodeWhite,decodeWhite,(///),breakup)whereimportData.Char(isSpace,chr,ord)importByteStringUtils(packStringToUTF8,unpackPSFromUTF8)importqualifiedData.ByteString.Char8asBC(unpack,pack)importqualifiedData.ByteStringasB(ByteString)newtypeFileName=FNFilePathderiving(Eq,Ord)instanceShowFileNamewhereshowsPrecd(FNfp)=showParen(d>appPrec)$showString"fp2fn ".showsPrec(appPrec+1)fpwhereappPrec=10{-# INLINE fp2fn #-}fp2fn::FilePath->FileNamefp2fnfp=FNfp{-# INLINE fn2fp #-}fn2fp::FileName->FilePathfn2fp(FNfp)=fp{-# INLINE niceps2fn #-}niceps2fn::B.ByteString->FileNameniceps2fn=FN.decodeWhite.BC.unpack{-# INLINE fn2niceps #-}fn2niceps::FileName->B.ByteStringfn2niceps(FNfp)=BC.pack$encodeWhitefp{-# INLINE fn2ps #-}fn2ps::FileName->B.ByteStringfn2ps(FNfp)=packStringToUTF8$encodeWhitefp{-# INLINE ps2fn #-}ps2fn::B.ByteString->FileNameps2fnps=FN$decodeWhite$unpackPSFromUTF8ps-- | 'encodeWhite' translates whitespace in filenames to a darcs-specific-- format (numerical representation according to 'ord' surrounded by-- backslashes). Note that backslashes are also escaped since they are used-- in the encoding.---- > encodeWhite "hello there" == "hello\32\there"-- > encodeWhite "hello\there" == "hello\92\there"encodeWhite::FilePath->StringencodeWhite(c:cs)|isSpacec||c=='\\'='\\':(show$ordc)++"\\"++encodeWhitecsencodeWhite(c:cs)=c:encodeWhitecsencodeWhite[]=[]-- | 'decodeWhite' interprets the Darcs-specific \"encoded\" filenames-- produced by 'encodeWhite'---- > decodeWhite "hello\32\there" == "hello there"-- > decodeWhite "hello\92\there" == "hello\there"-- > decodeWhite "hello\there" == error "malformed filename"decodeWhite::String->FilePathdecodeWhite('\\':cs)=casebreak(=='\\')csof(theord,'\\':rest)->chr(readtheord):decodeWhiterest_->error"malformed filename"decodeWhite(c:cs)=c:decodeWhitecsdecodeWhite""=""ownName::FileName->FileNameownName(FNf)=casebreakLast'/'fofNothing->FNfJust(_,f')->FNf'superName::FileName->FileNamesuperNamefn=casenormPathfnofFNf->casebreakLast'/'fofNothing->FN"."Just(d,_)->FNdbreakOnDir::FileName->Maybe(FileName,FileName)breakOnDir(FNp)=casebreakFirst'/'pofNothing->NothingJust(d,f)|d=="."->breakOnDir$FNf|otherwise->Just(FNd,FNf)normPath::FileName->FileName-- remove "./"normPath(FNp)=FN$repath$dropDotdot$breakupprepath::[String]->Stringrepath[]=""repath[f]=frepath(d:p)=d++"/"++repathpdropDotdot::[String]->[String]dropDotdot("":p)=dropDotdotpdropDotdot(".":p)=dropDotdotpdropDotdot("..":p)="..":(dropDotdotp)dropDotdot(_:"..":p)=dropDotdotpdropDotdot(d:p)=casedropDotdotpof("..":p')->p'p'->d:p'dropDotdot[]=[]-- | Split a file path at the slashesbreakup::String->[String]breakupp=casebreak(=='/')pof(d,"")->[d](d,p')->d:breakup(tailp')breakFirst::Char->String->Maybe(String,String)breakFirstcl=bf[]lwherebfa(r:rs)|r==c=Just(reversea,rs)|otherwise=bf(r:a)rsbf_[]=NothingbreakLast::Char->String->Maybe(String,String)breakLastcl=casebreakFirstc(reversel)ofNothing->NothingJust(a,b)->Just(reverseb,reversea)(///)::FileName->FileName->FileName(FN"")///b=normPathba///b=normPath$fp2fn$fn2fpa++"/"++fn2fpbmovedirfilename::FileName->FileName->FileName->FileNamemovedirfilenameoldnewname=ifname'==old'thennewelseiflengthname'>lengthold'&&take(lengthold'+1)name'==old'++"/"thenfp2fn("./"++new'++drop(lengthold')name')elsenamewhereold'=fn2fp$normPatholdnew'=fn2fp$normPathnewname'=fn2fp$normPathname