1 {-# LANGUAGE BangPatterns #-}
3 {-# LANGUAGE FlexibleContexts #-}
4 {-# LANGUAGE RankNTypes #-}
5 {-# LANGUAGE ScopedTypeVariables #-}
7 -----------------------------------------------------------------------------
10 -- Module : Distribution.Utils.Generic
11 -- Copyright : Isaac Jones, Simon Marlow 2003-2004
13 -- portions Copyright (c) 2007, Galois Inc.
15 -- Maintainer : cabal-devel@haskell.org
16 -- Portability : portable
18 -- A large and somewhat miscellaneous collection of utility functions used
19 -- throughout the rest of the Cabal lib and in other tools that use the Cabal
20 -- lib like @cabal-install@. It has a very simple set of logging actions. It
21 -- has low level functions for running programs, a bunch of wrappers for
22 -- various directory and file functions that do extra logging.
23 module Distribution
.Utils
.Generic
24 ( -- * reading and writing files safely
39 , withUTF8FileContents
46 , normaliseLineEndings
83 , isAbsoluteOnAnyPlatform
84 , isRelativeOnAnyPlatform
87 import Distribution
.Compat
.Prelude
90 import Distribution
.Utils
.String
92 import Data
.Bits
(shiftL
, (.&.), (.|
.))
93 import qualified Data
.ByteString
as SBS
94 import qualified Data
.ByteString
.Lazy
as LBS
98 import qualified Data
.Set
as Set
100 import qualified Control
.Exception
as Exception
101 import System
.Directory
103 , getTemporaryDirectory
107 import System
.FilePath
115 , openBinaryTempFileWithDefaultPermissions
120 -- -----------------------------------------------------------------------------
123 -- | Wraps text to the default line width. Existing newlines are preserved.
124 wrapText
:: String -> String
135 -- | Wraps a list of words to a list of lines of words of a particular width.
136 wrapLine
:: Int -> [String] -> [[String]]
137 wrapLine width
= wrap
0 []
139 wrap
:: Int -> [String] -> [String] -> [[String]]
141 |
length w
+ 1 > width
=
142 wrap
(length w
) [w
] ws
143 wrap col line
(w
: ws
)
144 | col
+ length w
+ 1 > width
=
145 reverse line
: wrap
0 [] (w
: ws
)
146 wrap col line
(w
: ws
) =
147 let col
' = col
+ length w
+ 1
148 in wrap col
' (w
: line
) ws
150 wrap _ line
[] = [reverse line
]
152 -----------------------------------
153 -- Safely reading and writing files
155 -- | Gets the contents of a file, but guarantee that it gets closed.
157 -- The file is read lazily; if it is not fully consumed by the action then an
158 -- exception is thrown.
159 withFileContents
:: FilePath -> (String -> IO a
) -> IO a
160 withFileContents name action
=
164 (\hnd
-> hGetContents hnd
>>= action
)
166 -- | Writes a file atomically.
168 -- The file is either written successfully or an IO exception is raised and
169 -- the original file is left unchanged.
173 -- - If the temp directory (@$TMPDIR@) is in a filesystem different than the
174 -- destination path, the renaming will be emulated via 'copyFile' then
179 -- - This operation is not guaranteed to be atomic, see 'renameFile'.
181 -- - It is not possible to delete a file that is open by a process. This case
182 -- will give an IO exception but the atomic property is not affected.
184 -- - If the temp directory (@TMP@/@TEMP@/..., see haddocks on
185 -- 'getTemporaryDirectory') is in a different drive than the destination path,
186 -- the write will be emulated via 'copyFile', then 'deleteFile'.
187 writeFileAtomic
:: FilePath -> LBS
.ByteString
-> IO ()
188 writeFileAtomic targetPath content
= do
189 let targetFile
= takeFileName targetPath
190 tmpDir
<- getTemporaryDirectory
191 Exception
.bracketOnError
192 (openBinaryTempFileWithDefaultPermissions tmpDir
$ targetFile
<.> "tmp")
193 (\(tmpPath
, handle
) -> hClose handle
>> removeFile tmpPath
)
194 ( \(tmpPath
, handle
) -> do
195 LBS
.hPut handle content
198 (renameFile tmpPath targetPath
)
199 ( \(_
:: Exception
.SomeException
) -> do
200 copyFile tmpPath targetPath
205 -- ------------------------------------------------------------
209 -- ------------------------------------------------------------
211 -- | Decode 'String' from UTF8-encoded 'BS.ByteString'
213 -- Invalid data in the UTF8 stream (this includes code-points @U+D800@
214 -- through @U+DFFF@) will be decoded as the replacement character (@U+FFFD@).
215 fromUTF8BS
:: SBS
.ByteString
-> String
216 fromUTF8BS
= decodeStringUtf8
. SBS
.unpack
218 -- | Variant of 'fromUTF8BS' for lazy 'BS.ByteString's
219 fromUTF8LBS
:: LBS
.ByteString
-> String
220 fromUTF8LBS
= decodeStringUtf8
. LBS
.unpack
222 -- | Encode 'String' to UTF8-encoded 'SBS.ByteString'
224 -- Code-points in the @U+D800@-@U+DFFF@ range will be encoded
225 -- as the replacement character (i.e. @U+FFFD@).
226 toUTF8BS
:: String -> SBS
.ByteString
227 toUTF8BS
= SBS
.pack
. encodeStringUtf8
229 -- | Variant of 'toUTF8BS' for lazy 'BS.ByteString's
230 toUTF8LBS
:: String -> LBS
.ByteString
231 toUTF8LBS
= LBS
.pack
. encodeStringUtf8
233 -- | Check that strict 'ByteString' is valid UTF8. Returns 'Just offset' if it's not.
234 validateUTF8
:: SBS
.ByteString
-> Maybe Int
237 go off bs
= case SBS
.uncons bs
of
240 | c
<= 0x7F -> go
(off
+ 1) bs
'
241 | c
<= 0xBF -> Just off
242 | c
<= 0xDF -> twoBytes off c bs
'
243 | c
<= 0xEF -> moreBytes off
3 0x800 bs
' (fromIntegral $ c
.&. 0xF)
244 | c
<= 0xF7 -> moreBytes off
4 0x10000 bs
' (fromIntegral $ c
.&. 0x7)
245 | c
<= 0xFB -> moreBytes off
5 0x200000 bs
' (fromIntegral $ c
.&. 0x3)
246 | c
<= 0xFD -> moreBytes off
6 0x4000000 bs
' (fromIntegral $ c
.&. 0x1)
247 |
otherwise -> Just off
249 twoBytes off c0 bs
= case SBS
.uncons bs
of
252 | c1
.&. 0xC0 == 0x80 ->
253 if d
>= (0x80 :: Int)
254 then go
(off
+ 2) bs
'
256 |
otherwise -> Just off
258 d
= (fromIntegral (c0
.&. 0x1F) `shiftL`
6) .|
. fromIntegral (c1
.&. 0x3F)
260 moreBytes
:: Int -> Int -> Int -> SBS
.ByteString
-> Int -> Maybe Int
261 moreBytes off
1 overlong cs
' acc
264 , acc
< 0xD800 ||
0xDFFF < acc
=
268 moreBytes off byteCount overlong bs acc
= case SBS
.uncons bs
of
270 | cn
.&. 0xC0 == 0x80 ->
271 moreBytes
(off
+ 1) (byteCount
- 1) overlong bs
' ((acc `shiftL`
6) .|
. fromIntegral cn
.&. 0x3F)
274 -- | Ignore a Unicode byte order mark (BOM) at the beginning of the input
275 ignoreBOM
:: String -> String
276 ignoreBOM
('\xFEFF
' : string) = string
277 ignoreBOM
string = string
279 -- | Reads a UTF8 encoded text file as a Unicode String
281 -- Reads lazily using ordinary 'readFile'.
282 readUTF8File
:: FilePath -> IO String
283 readUTF8File f
= (ignoreBOM
. fromUTF8LBS
) <$> LBS
.readFile f
285 -- | Reads a UTF8 encoded text file as a Unicode String
287 -- Same behaviour as 'withFileContents'.
288 withUTF8FileContents
:: FilePath -> (String -> IO a
) -> IO a
289 withUTF8FileContents name action
=
293 (\hnd
-> LBS
.hGetContents hnd
>>= action
. ignoreBOM
. fromUTF8LBS
)
295 -- | Writes a Unicode String as a UTF8 encoded text file.
297 -- Uses 'writeFileAtomic', so provides the same guarantees.
298 writeUTF8File
:: FilePath -> String -> IO ()
299 writeUTF8File path
= writeFileAtomic path
. toUTF8LBS
301 -- | Fix different systems silly line ending conventions
302 normaliseLineEndings
:: String -> String
303 normaliseLineEndings
[] = []
304 normaliseLineEndings
('\r' : '\n' : s
) = '\n' : normaliseLineEndings s
-- windows
305 normaliseLineEndings
('\r' : s
) = '\n' : normaliseLineEndings s
-- old OS X
306 normaliseLineEndings
(c
: s
) = c
: normaliseLineEndings s
308 -- ------------------------------------------------------------
312 -- ------------------------------------------------------------
314 -- | @dropWhileEndLE p@ is equivalent to @reverse . dropWhile p . reverse@, but
315 -- quite a bit faster. The difference between "Data.List.dropWhileEnd" and this
316 -- version is that the one in "Data.List" is strict in elements, but spine-lazy,
317 -- while this one is spine-strict but lazy in elements. That's what @LE@ stands
318 -- for - "lazy in elements".
322 -- >>> safeTail $ Data.List.dropWhileEnd (<3) [undefined, 5, 4, 3, 2, 1]
323 -- *** Exception: Prelude.undefined
326 -- >>> safeTail $ dropWhileEndLE (<3) [undefined, 5, 4, 3, 2, 1]
329 -- >>> take 3 $ Data.List.dropWhileEnd (<3) [5, 4, 3, 2, 1, undefined]
332 -- >>> take 3 $ dropWhileEndLE (<3) [5, 4, 3, 2, 1, undefined]
333 -- *** Exception: Prelude.undefined
335 dropWhileEndLE
:: (a
-> Bool) -> [a
] -> [a
]
336 dropWhileEndLE p
= foldr (\x r
-> if null r
&& p x
then [] else x
: r
) []
338 -- | @takeWhileEndLE p@ is equivalent to @reverse . takeWhile p . reverse@, but
339 -- is usually faster (as well as being easier to read).
340 takeWhileEndLE
:: (a
-> Bool) -> [a
] -> [a
]
341 takeWhileEndLE p
= fst . foldr go
([], False)
344 |
not done
&& p x
= (x
: rest
, False)
345 |
otherwise = (rest
, True)
347 -- | Like 'Data.List.nub', but has @O(n log n)@ complexity instead of
348 -- @O(n^2)@. Code for 'ordNub' and 'listUnion' taken from Niklas Hambüchen's
349 -- <http://github.com/nh2/haskell-ordnub ordnub> package.
350 ordNub
:: Ord a
=> [a
] -> [a
]
353 -- | Like 'ordNub' and 'Data.List.nubBy'. Selects a key for each element and
354 -- takes the nub based on that key.
355 ordNubBy
:: Ord b
=> (a
-> b
) -> [a
] -> [a
]
356 ordNubBy f l
= go Set
.empty l
360 | y `Set
.member` s
= go s xs
362 let !s
' = Set
.insert y s
367 -- | Like "Data.List.union", but has @O(n log n)@ complexity instead of
369 listUnion
:: Ord a
=> [a
] -> [a
] -> [a
]
370 listUnion a b
= a
++ ordNub
(filter (`Set
.notMember` aSet
) b
)
372 aSet
= Set
.fromList a
374 -- | A right-biased version of 'ordNub'.
378 -- >>> ordNub [1,2,1] :: [Int]
381 -- >>> ordNubRight [1,2,1] :: [Int]
383 ordNubRight
:: Ord a
=> [a
] -> [a
]
384 ordNubRight
= fst . foldr go
([], Set
.empty)
389 else (x
: l
, Set
.insert x s
)
391 -- | A right-biased version of 'listUnion'.
395 -- >>> listUnion [1,2,3,4,3] [2,1,1]
398 -- >>> listUnionRight [1,2,3,4,3] [2,1,1]
400 listUnionRight
:: Ord a
=> [a
] -> [a
] -> [a
]
401 listUnionRight a b
= ordNubRight
(filter (`Set
.notMember` bSet
) a
) ++ b
403 bSet
= Set
.fromList b
405 -- | A total variant of 'head'.
408 safeHead
:: [a
] -> Maybe a
409 safeHead
[] = Nothing
410 safeHead
(x
: _
) = Just x
412 -- | A total variant of 'tail'.
415 safeTail
:: [a
] -> [a
]
417 safeTail
(_
: xs
) = xs
419 -- | A total variant of 'last'.
422 safeLast
:: [a
] -> Maybe a
423 safeLast
[] = Nothing
424 safeLast
(x
: xs
) = Just
(foldl (\_ a
-> a
) x xs
)
426 -- | A total variant of 'init'.
429 safeInit
:: [a
] -> [a
]
432 safeInit
(x
: xs
) = x
: safeInit xs
434 equating
:: Eq a
=> (b
-> a
) -> b
-> b
-> Bool
435 equating p x y
= p x
== p y
437 -- | Lower case string
439 -- >>> lowercase "Foobar"
441 lowercase
:: String -> String
442 lowercase
= map toLower
444 -- | Ascii characters
445 isAscii :: Char -> Bool
446 isAscii c
= fromEnum c
< 0x80
449 isAsciiAlpha
:: Char -> Bool
451 ('a
' <= c
&& c
<= 'z
')
452 ||
('A
' <= c
&& c
<= 'Z
')
454 -- | Ascii letters and digits.
456 -- >>> isAsciiAlphaNum 'a'
459 -- >>> isAsciiAlphaNum 'ä'
461 isAsciiAlphaNum
:: Char -> Bool
462 isAsciiAlphaNum c
= isAscii c
&& isAlphaNum c
464 unintersperse
:: Char -> String -> [String]
465 unintersperse mark
= unfoldr unintersperse1
470 let (this
, rest
) = break (== mark
) str
471 in Just
(this
, safeTail rest
)
473 -- | Like 'break', but with 'Maybe' predicate
475 -- >>> breakMaybe (readMaybe :: String -> Maybe Int) ["foo", "bar", "1", "2", "quu"]
476 -- (["foo","bar"],Just (1,["2","quu"]))
478 -- >>> breakMaybe (readMaybe :: String -> Maybe Int) ["foo", "bar"]
479 -- (["foo","bar"],Nothing)
482 breakMaybe
:: (a
-> Maybe b
) -> [a
] -> ([a
], Maybe (b
, [a
]))
485 go
!acc
[] = (acc
[], Nothing
)
486 go
!acc
(x
: xs
) = case f x
of
487 Nothing
-> go
(acc
. (x
:)) xs
488 Just b
-> (acc
[], Just
(b
, xs
))
490 -- | Like 'span' but with 'Maybe' predicate
492 -- >>> spanMaybe listToMaybe [[1,2],[3],[],[4,5],[6,7]]
493 -- ([1,3],[[],[4,5],[6,7]])
495 -- >>> spanMaybe (readMaybe :: String -> Maybe Int) ["1", "2", "foo"]
499 spanMaybe
:: (a
-> Maybe b
) -> [a
] -> ([b
], [a
])
500 spanMaybe _ xs
@[] = ([], xs
)
501 spanMaybe p xs
@(x
: xs
') = case p x
of
502 Just y
-> let (ys
, zs
) = spanMaybe p xs
' in (y
: ys
, zs
)
505 -- | 'unfoldr' with monadic action.
507 -- >>> take 5 $ unfoldrM (\b r -> Just (r + b, b + 1)) (1 :: Int) 2
511 unfoldrM
:: Monad m
=> (b
-> m
(Maybe (a
, b
))) -> b
-> m
[a
]
518 Just
(a
, b
') -> liftM (a
:) (go b
')
520 -- | The opposite of 'snoc', which is the reverse of 'cons'
524 -- >>> unsnoc [1, 2, 3]
531 unsnoc
:: [a
] -> Maybe ([a
], a
)
533 unsnoc
(x
: xs
) = Just
(unsnocNE
(x
:| xs
))
535 -- | Like 'unsnoc', but for 'NonEmpty' so without the 'Maybe'
539 -- >>> unsnocNE (1 :| [2, 3])
542 -- >>> unsnocNE (1 :| [])
546 unsnocNE
:: NonEmpty a
-> ([a
], a
)
547 unsnocNE
(x
:| xs
) = go x xs
550 go y
(z
: zs
) = let ~
(ws
, w
) = go z zs
in (y
: ws
, w
)
552 -------------------------------------------------------------------------------
554 -------------------------------------------------------------------------------
557 fstOf3
:: (a
, b
, c
) -> a
561 sndOf3
:: (a
, b
, c
) -> b
565 trdOf3
:: (a
, b
, c
) -> c
568 -- ------------------------------------------------------------
572 -- ------------------------------------------------------------
574 -- | 'isAbsoluteOnAnyPlatform' and 'isRelativeOnAnyPlatform' are like
575 -- 'System.FilePath.isAbsolute' and 'System.FilePath.isRelative' but have
576 -- platform independent heuristics.
577 -- The System.FilePath exists in two versions, Windows and Posix. The two
578 -- versions don't agree on what is a relative path and we don't know if we're
579 -- given Windows or Posix paths.
580 -- This results in false positives when running on Posix and inspecting
581 -- Windows paths, like the hackage server does.
582 -- System.FilePath.Posix.isAbsolute \"C:\\hello\" == False
583 -- System.FilePath.Windows.isAbsolute \"/hello\" == False
584 -- This means that we would treat paths that start with \"/\" to be absolute.
585 -- On Posix they are indeed absolute, while on Windows they are not.
587 -- The portable versions should be used when we might deal with paths that
588 -- are from another OS than the host OS. For example, the Hackage Server
589 -- deals with both Windows and Posix paths while performing the
590 -- PackageDescription checks. In contrast, when we run 'cabal configure' we
591 -- do expect the paths to be correct for our OS and we should not have to use
592 -- the platform independent heuristics.
593 isAbsoluteOnAnyPlatform
:: FilePath -> Bool
595 isAbsoluteOnAnyPlatform
(drive
: ':' : '\\' : _
) = isAlpha drive
596 isAbsoluteOnAnyPlatform
(drive
: ':' : '/' : _
) = isAlpha drive
598 isAbsoluteOnAnyPlatform
('\\' : '\\' : _
) = True
600 isAbsoluteOnAnyPlatform
('/' : _
) = True
601 isAbsoluteOnAnyPlatform _
= False
603 -- | @isRelativeOnAnyPlatform = not . 'isAbsoluteOnAnyPlatform'@
604 isRelativeOnAnyPlatform
:: FilePath -> Bool
605 isRelativeOnAnyPlatform
= not . isAbsoluteOnAnyPlatform
608 -- >>> import Data.Maybe
609 -- >>> import Text.Read