summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2010-11-02 15:54:43 -0400
committerGravatar Joey Hess <joey@kitenet.net>2010-11-02 15:54:43 -0400
commit9e5985ff983c4b489d545e99946cf62e34fadcd9 (patch)
treef2c0772df6ddf39ece4ae5f3f8d3ceac205a2532
parent82056a79213268c4d7d93c64c013ca44056f70ba (diff)
blew several hours on getting the decodeGitFile 100% right with quickcheck
-rw-r--r--GitRepo.hs76
1 files changed, 64 insertions, 12 deletions
diff --git a/GitRepo.hs b/GitRepo.hs
index 9fda0a23f..c62a820d8 100644
--- a/GitRepo.hs
+++ b/GitRepo.hs
@@ -36,7 +36,11 @@ module GitRepo (
inRepo,
notInRepo,
stagedFiles,
- checkAttr
+ checkAttr,
+ decodeGitFile,
+ encodeGitFile,
+
+ prop_idempotent_encode
) where
import Monad (unless)
@@ -51,6 +55,9 @@ import qualified Data.Map as Map hiding (map, split)
import Network.URI
import Maybe
import Char
+import Text.Printf
+import Data.Word (Word8)
+import Codec.Binary.UTF8.String (encode)
import Utility
@@ -332,25 +339,70 @@ checkAttr repo attr files = do
decodeGitFile :: String -> FilePath
decodeGitFile [] = []
decodeGitFile f@(c:s)
- | c == '"' = unescape middle
+ | c == '"' = unescape ("", middle)
| otherwise = f
where
- e = "\\"
middle = take (length s - 1) s
- unescape v = foldl (++) beginning $ map decode $ split e rest
+ unescape (b, []) = b
+ unescape (b, v) = b ++ beginning ++ unescape (decode rest)
where
pair = span (/= '\\') v
beginning = fst pair
rest = snd pair
- decode [] = ""
- decode n
- | length num == 3 = (chr $ readoctal num):rest
- | otherwise = e++n
+ isescape c = c == '\\'
+ decode (e:n1:n2:n3:rest)
+ | isescape e && alloctal = (fromoctal, rest)
+ where
+ alloctal = isOctDigit n1 &&
+ isOctDigit n2 &&
+ isOctDigit n3
+ fromoctal = [chr $ readoctal (n1:n2:n3:[])]
+ readoctal o = read $ "0o" ++ o :: Int
+ decode (e:nc:rest)
+ | isescape e = ([echar nc], rest)
where
- pair = span isOctDigit n
- num = fst pair
- rest = snd pair
- readoctal o = read $ "0o" ++ o :: Int
+ -- special character escapes
+ echar 'a' = '\a'
+ echar 'b' = '\b'
+ echar 'f' = '\f'
+ echar 'n' = '\n'
+ echar 'r' = '\r'
+ echar 't' = '\t'
+ echar 'v' = '\v'
+ echar x = x
+ decode n = ("", n)
+
+{- Should not need to use this, except for testing decodeGitFile. -}
+encodeGitFile :: FilePath -> String
+encodeGitFile s = (foldl (++) "\"" (map echar s)) ++ "\""
+ where
+ e c = "\\" ++ [c]
+ echar '\a' = e 'a'
+ echar '\b' = e 'b'
+ echar '\f' = e 'f'
+ echar '\n' = e 'n'
+ echar '\r' = e 'r'
+ echar '\t' = e 't'
+ echar '\v' = e 'v'
+ echar '\\' = e '\\'
+ echar '"' = e '"'
+ echar x
+ | ord x < 0x20 = e_num x -- low ascii
+ | ord x >= 256 = e_utf x
+ | ord x > 0x7E = e_num x -- high ascii
+ | otherwise = [x] -- printable ascii
+ where
+ showoctal i = "\\" ++ (printf "%03o" i)
+ e_num c = showoctal $ ord c
+ -- unicode character is decomposed to Word8
+ -- and each is shown with e_num
+ e_utf c = foldl (++) "" $ map showoctal $
+ (encode [c] :: [Word8])
+
+
+{- for quickcheck -}
+prop_idempotent_encode :: String -> Bool
+prop_idempotent_encode s = s == (decodeGitFile $ encodeGitFile s)
{- Finds the current git repository, which may be in a parent directory. -}
repoFromCwd :: IO Repo