-- 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.moduleDarcs.Patch.Check(PatchCheck(),do_check,file_exists,dir_exists,remove_file,remove_dir,create_file,create_dir,insert_line,delete_line,is_valid,do_verbose_check,file_empty,check_move,modify_file,Possibly(..))whereimportText.Regex(mkRegex,matchRegex)importSystem.IO.Unsafe(unsafePerformIO)importqualifiedData.ByteStringasB(ByteString)importData.List(isPrefixOf)newtypePatchChecka=PC(KnownState->(KnownState,a))dataPossiblya=PJusta|PNothing|PSomethingderiving(Eq,Show)dataProp=FileExString|DirExString|NotExString|FileLinesString[PossiblyB.ByteString]deriving(Eq)dataKnownState=P[Prop][Prop]|Inconsistentderiving(Show)instanceShowPropwhereshow(FileExf)="FileEx "++fshow(DirExd)="DirEx "++dshow(NotExf)="NotEx"++fshow(FileLinesfl)="FileLines "++f++" "++show(take10l)instanceMonadPatchCheckwhere(PCp)>>=k=PC(\s0->let(s1,a)=ps0(PCq)=kainqs1)returna=PC(\s->(s,a))do_check::PatchChecka->ado_check(PCp)=snd$p(P[][])do_verbose_check::PatchChecka->ado_verbose_check(PCp)=casep(P[][])of(pc,b)->unsafePerformIO$doputStrLn$showpcreturnbis_valid::PatchCheckBoolis_valid=PCivwhereivInconsistent=(Inconsistent,False)ivm=(m,True)has::Prop->[Prop]->Boolhas_[]=Falsehask(k':ks)=k==k'||haskksmodify_file::String->([PossiblyB.ByteString]->Maybe[PossiblyB.ByteString])->PatchCheckBoolmodify_filefchange=dofile_existsfc<-file_contentsfcasechangecofNothing->assert_not$FileExf-- shorthand for "FAIL"Justc'->doset_contentsfc'is_validinsert_line::String->Int->B.ByteString->PatchCheckBoolinsert_linefnl=doc<-file_contentsfcaseilncof[]->assert_not$FileExfc'->doset_contentsfc'returnTruewhereil1mls=(PJustl:mls)ili(ml:mls)=ml:il(i-1)mlsil_[]=[]-- deletes a line from a hunk patch (third argument) in the given file (first-- argument) at the given line number (second argument)delete_line::String->Int->B.ByteString->PatchCheckBooldelete_linefnl=doc<-file_contentsfcasedl[]ncofNothing->assert_not$FileExfJustc'->doset_contentsfc'is_validwheredl__[]=Nothingdlo1(ml':ls)=caseml'ofPSomething->Just$reverseo++lsPNothing->Just$reverseo++lsPJustl'->ifl'==lthenJust$reverseo++lselseNothingdloi(ml:mls)=casemlofPNothing->dl(PSomething:o)(i-1)mls_->dl(ml:o)(i-1)mlsset_contents::String->[PossiblyB.ByteString]->PatchCheck()set_contentsfmss=PC(scfmss)sc::String->[PossiblyB.ByteString]->KnownState->(KnownState,())scfmss(Pksnots)=(P(scl[]fmssks)nots,())sc__Inconsistent=(Inconsistent,())scl::[Prop]->String->[PossiblyB.ByteString]->[Prop]->[Prop]scloldsfmss[]=FileLinesfmss:oldsscloldsfmss(FileLinesf'mss':ks)|f==f'=FileLinesfmss:(olds++ks)|f/=f'=scl(FileLinesf'mss':olds)fmssksscloldsfmss(k:ks)=scl(k:olds)fmssksfile_contents::String->PatchCheck[PossiblyB.ByteString]file_contentsf=PCfcwherefcInconsistent=(Inconsistent,[])fc(Pksnots)=(Pksnots,ficks)fic(FileLinesf'mss:_)|f==f'=mssfic(_:ks)=ficksfic[]=repeatPNothingfile_empty::String->PatchCheckBoolfile_emptyf=doc<-file_contentsfletempty=all(PNothing==)$take101cifemptythendoset_contentsf[]is_valid-- Crude way to make it inconsistent and return false:elseassert_not$FileExfreturnemptymovedirfilename::String->String->String->Stringmovedirfilenamedd'f=if(d++"/")`isPrefixOf`fthend'++drop(lengthd)felseiff==dthend'elsefdo_swap::String->String->PatchCheckBooldo_swapff'=PCswfnwhereswfnInconsistent=(Inconsistent,False)swfn(Pksnots)=(P(mapswks)(mapswnots),True)sw(FileExa)|f`is_soe`a=FileEx$movedirfilenameff'a|f'`is_soe`a=FileEx$movedirfilenamef'fasw(DirExa)|f`is_soe`a=DirEx$movedirfilenameff'a|f'`is_soe`a=DirEx$movedirfilenamef'fasw(FileLinesals)|f`is_soe`a=FileLines(movedirfilenameff'a)ls|f'`is_soe`a=FileLines(movedirfilenamef'fa)lssw(NotExa)|f`is_soe`a=NotEx$movedirfilenameff'a|f'`is_soe`a=NotEx$movedirfilenamef'faswp=pis_soed1d2=-- is_superdir_or_equald1==d2||(d1++"/")`isPrefixOf`d2assert::Prop->PatchCheckBoolassertp=PCassertfnwhereassertfnInconsistent=(Inconsistent,False)assertfn(Pksnots)=ifhaspnotsthen(Inconsistent,False)elseifhaspksthen(Pksnots,True)else(P(p:ks)nots,True)assert_not::Prop->PatchCheckBoolassert_notp=PCassertnfnwhereassertnfnInconsistent=(Inconsistent,False)assertnfn(Pksnots)=ifhaspksthen(Inconsistent,False)elseifhaspnotsthen(Pksnots,True)else(Pks(p:nots),True)change_to_true::Prop->PatchCheckBoolchange_to_truep=PCchtfnwherechtfnInconsistent=(Inconsistent,False)chtfn(Pksnots)=(P(p:ks)(filter(p/=)nots),True)change_to_false::Prop->PatchCheckBoolchange_to_falsep=PCchffnwherechffnInconsistent=(Inconsistent,False)chffn(Pksnots)=(P(filter(p/=)ks)(p:nots),True)assert_file_exists::String->PatchCheckBoolassert_file_existsf=doassert_not$NotExfassert_not$DirExfassert$FileExfassert_dir_exists::String->PatchCheckBoolassert_dir_existsd=doassert_not$NotExdassert_not$FileExdassert$DirExdassert_exists::String->PatchCheckBoolassert_existsf=assert_not$NotExfassert_no_such::String->PatchCheckBoolassert_no_suchf=doassert_not$FileExfassert_not$DirExfassert$NotExfcreate_file::String->PatchCheckBoolcreate_filefn=dosuperdirs_existfnassert_no_suchfnchange_to_true(FileExfn)change_to_false(NotExfn)create_dir::String->PatchCheckBoolcreate_dirfn=dosubstuff_dont_existfnsuperdirs_existfnassert_no_suchfnchange_to_true(DirExfn)change_to_false(NotExfn)remove_file::String->PatchCheckBoolremove_filefn=dosuperdirs_existfnassert_file_existsfnfile_emptyfnchange_to_false(FileExfn)change_to_true(NotExfn)remove_dir::String->PatchCheckBoolremove_dirfn=dosubstuff_dont_existfnsuperdirs_existfnassert_dir_existsfnchange_to_false(DirExfn)change_to_true(NotExfn)check_move::String->String->PatchCheckBoolcheck_moveff'=dosuperdirs_existfsuperdirs_existf'assert_existsfassert_no_suchf'do_swapff'substuff_dont_exist::String->PatchCheckBoolsubstuff_dont_existd=PCssdewheressdeInconsistent=(Inconsistent,False)ssde(Pksnots)=ifallnossksthen(Pksnots,True)else(Inconsistent,False)wherenoss(FileExf)=not(is_within_dirf)noss(DirExf)=not(is_within_dirf)noss_=Trueis_within_dirf=(d++"/")`isPrefixOf`fsuperdirs_exist::String->PatchCheckBoolsuperdirs_existfn=casematchRegex(mkRegex"\\./(.+)/[^/]+")fnofJust["."]->returnTrueJust[d]->doa<-assert_dir_exists("./"++d)b<-superdirs_exist("./"++d)return$!a&&b_->is_validfile_exists::String->PatchCheckBoolfile_existsfn=dosuperdirs_existfnassert_file_existsfndir_exists::String->PatchCheckBooldir_existsfn=dosuperdirs_existfnassert_dir_existsfn