1{- git check-ignore interface
2 -
3 - Copyright 2013 Joey Hess <id@joeyh.name>
4 -
5 - Licensed under the GNU AGPL version 3 or higher.
6 -}
7
8module Git.CheckIgnore (
9	CheckIgnoreHandle,
10	checkIgnoreStart,
11	checkIgnoreStop,
12	checkIgnored
13) where
14
15import Common
16import Git
17import Git.Command
18import qualified Utility.CoProcess as CoProcess
19
20import System.IO.Error
21import qualified Data.ByteString as B
22
23type CheckIgnoreHandle = CoProcess.CoProcessHandle
24
25{- Starts git check-ignore running, and returns a handle.
26 -
27 - This relies on git check-ignore --non-matching -v outputting
28 - lines for both matching an non-matching files. Also relies on
29 - GIT_FLUSH behavior flushing the output buffer when git check-ignore
30 - is piping to us.
31 -
32 - check-ignore does not support --literal-pathspecs, so remove that
33 - from the gitGlobalOpts if set.
34 -}
35checkIgnoreStart :: Repo -> IO CheckIgnoreHandle
36checkIgnoreStart repo = gitCoProcessStart True params repo'
37  where
38	params =
39		[ Param "check-ignore"
40		, Param "-z"
41		, Param "--stdin"
42		, Param "--verbose"
43		, Param "--non-matching"
44		]
45	repo' = repo { gitGlobalOpts = filter (not . pathspecs) (gitGlobalOpts repo) }
46	pathspecs (Param "--literal-pathspecs") = True
47	pathspecs _ = False
48
49{- For some reason, check-ignore --batch always exits nonzero,
50 - so ignore any error. -}
51checkIgnoreStop :: CheckIgnoreHandle -> IO ()
52checkIgnoreStop = void . tryIO . CoProcess.stop
53
54{- Returns True if a file is ignored. -}
55checkIgnored :: CheckIgnoreHandle -> RawFilePath -> IO Bool
56checkIgnored h file = CoProcess.query h send (receive "")
57  where
58	send to = do
59		B.hPutStr to $ file `B.snoc` 0
60		hFlush to
61	receive c from = do
62		s <- hGetSomeString from 1024
63		if null s
64			then eofError
65			else do
66				let v = c ++ s
67				maybe (receive v from) return (parse v)
68	parse s = case segment (== '\0') s of
69		(_source:_line:pattern:_pathname:_eol:[]) -> Just $ not $ null pattern
70		_ -> Nothing
71	eofError = ioError $ mkIOError userErrorType "git cat-file EOF" Nothing Nothing
72