-- Copyright (C) 2005 David Roundy---- This file is licensed under the GPL, version two or later.{-# LANGUAGE CPP #-}moduleDarcs.Repository.Format(RepoFormat(..),RepoProperty(..),identifyRepoFormat,tryIdentifyRepoFormat,createRepoFormat,writeRepoFormat,writeProblem,readProblem,readfromAndWritetoProblem,formatHas,)whereimportData.Maybe(isJust,mapMaybe)importControl.Monad(mplus,(<=<))importDarcs.SignalHandler(catchNonSignal)importDarcs.External(fetchFilePS,Cachable(Cachable))importDarcs.Flags(DarcsFlag(UseFormat2,UseHashedInventory,UseNoWorkingDir))importDarcs.Lock(writeBinFile)importDarcs.Utils(catchall,prettyException)importProgress(beginTedious,endTedious,finishedOneIO)importDarcs.Global(darcsdir)importByteStringUtils(linesPS)importqualifiedData.ByteString.Char8asBC(split,unpack,singleton,elemIndex,pack)importqualifiedData.ByteStringasB(ByteString,null,empty)importqualifiedByteStringUtilsasBU(intercalate)#include "impossible.h"dataRepoProperty=Darcs1_0|Darcs2|HashedInventory|NoWorkingDir-- | @RepoFormat@ is the representation of the format of a-- repository. Each sublist corresponds to a line in the format-- file. Each line is decomposed into words.newtypeRepoFormat=RF[[B.ByteString]]deriving(Show)-- | The file where the format information should be.df::FilePathdf=darcsdir++"/format"-- | @identifyRepoFormat URL@ identifies the format of the repository-- at the given address. Fails if we weren't able to identify the format.identifyRepoFormat::String->IORepoFormatidentifyRepoFormat=eitherfailreturn<=<tryIdentifyRepoFormat-- | @tryIdentifyRepoFormat URL@ identifies the format of the repository-- at the given address. Return @Left reason@ if it fails, where-- @reason@ explains why we weren't able to identify the format.tryIdentifyRepoFormat::String->IO(EitherStringRepoFormat)tryIdentifyRepoFormatrepo=doletk="Identifying repository "++repobeginTediouskfinishedOneIOk"format"dff<-fetchFilePS(repo++"/"++df)Cachable`catchall`returnB.empty-- below is a workaround for servers that don't return a 404 on nonexistent filesrf<-ifB.nulldff||isJust(BC.elemIndex'<'dff)thendofinishedOneIOk"inventory"have_inventory<-doesRemoteFileExist(repo++"/"++darcsdir++"/inventory")return$casehave_inventoryofRight_->RightdefaultRepoFormatLefte->Left.unlines$["Not a repository: "++repo++" ("++e++")","","HINT: Do you have the right URI for the repository?",""," If so, check with the repository owner to see if the following files"," are readable:",""," 1. _darcs/format - might not exist; that's OK"," 2. _darcs/inventory - should exist if #1 is missing"," 3. _darcs/hashed_inventory - should exist if #2 is missing"]elsereturn$Right$parseRepoFormatdffendTediouskreturnrfwheredrfex=fetchFilePSxCachable>>returnTruedoesRemoteFileExistx=fmapRight(drfex)`catchNonSignal`(\e->return(Left(prettyExceptione)))-- | @writeRepoFormat@ writes the repo format to the given file.writeRepoFormat::RepoFormat->FilePath->IO()writeRepoFormat(RFrf)loc=writeBinFileloc$unlines$map(BC.unpack.BU.intercalate(BC.singleton'|'))rfparseRepoFormat::B.ByteString->RepoFormatparseRepoFormatps=RF$map(BC.split'|')$filter(not.B.null)$linesPSps-- | The repo format we assume if we do not find a format file.defaultRepoFormat::RepoFormatdefaultRepoFormat=RF[[rp2psDarcs1_0]]createRepoFormat::[DarcsFlag]->RepoFormatcreateRepoFormatfs=RF(maprp2ps(HashedInventory:flags2wd):maybe2)wheremaybe2=ifUseFormat2`notElem`fs&&(UseHashedInventory`elem`fs)then[]else[[rp2psDarcs2]]flags2wd=ifUseNoWorkingDir`elem`fsthen[NoWorkingDir]else[]-- | @writeProblem form@ tells if we can write to a repo in format @form@.-- It returns @Nothing@ if there's no problem writing to such a repository.writeProblem::RepoFormat->MaybeStringwriteProblemrf=readProblemrf`mplus`allProblemsrfwpwherewpx|allisKnownx=Nothingwp[]=impossiblewpx=Just$unwords$"Can't write repository format: ":mapBC.unpack(filter(not.isKnown)x)-- | @readfromAndWritetoProblem form@ tells if we can read from and write to a repo in-- format @form@. It returns @Nothing@ if there's no problem reading-- and writing to such a repository.readfromAndWritetoProblem::RepoFormat->RepoFormat->MaybeStringreadfromAndWritetoProbleminrfoutrf|formatHasDarcs2inrf/=formatHasDarcs2outrf=Just"Cannot mix darcs-2 repositories with older formats"|otherwise=readProbleminrf`mplus`writeProblemoutrf-- | @readProblem form@ tells if we can read from a repo in format @form@.-- It returns @Nothing@ if there's no problem reading from such a repository.readProblem::RepoFormat->MaybeStringreadProblemrf|formatHasDarcs1_0rf&&formatHasDarcs2rf=Just"Invalid repositoryformat: format 2 is incompatible with format 1"readProblemrf=allProblemsrfrpwhererpx|anyisKnownx=Nothingrp[]=impossiblerpx=Just$unwords$"Can't understand repository format:":mapBC.unpackxallProblems::RepoFormat->([B.ByteString]->MaybeString)->MaybeStringallProblems(RFks)repoFormatLineProblem=maybeSingleError$mapMayberepoFormatLineProblemkswheremaybeSingleError[]=NothingmaybeSingleErrorxs=Just$unlinesxs-- | Does this version of darcs know how to handle this property?isKnown::B.ByteString->BoolisKnownp=p`elem`maprp2psknownProperties-- | This is the list of properties which this version of darcs knows-- how to handle.knownProperties::[RepoProperty]knownProperties=[Darcs1_0,Darcs2,HashedInventory,NoWorkingDir]formatHas::RepoProperty->RepoFormat->BoolformatHasf(RFks)=rp2psf`elem`concatksinstanceShowRepoPropertywhereshowDarcs1_0="darcs-1.0"showDarcs2="darcs-2"showHashedInventory="hashed"showNoWorkingDir="no-working-dir"rp2ps::RepoProperty->B.ByteStringrp2ps=BC.pack.show