2 % (c) The University of Glasgow 1992-2002
4 \section[Util]{Highly random utility functions}
9 -- general list processing
10 zipEqual, zipWithEqual, zipWith3Equal, zipWith4Equal,
11 zipLazy, stretchZipWith,
12 mapAndUnzip, mapAndUnzip3,
14 lengthExceeds, lengthIs, lengthAtLeast, listLengthCmp, atLength,
26 -- transitive closures
30 mapAccumL, mapAccumR, mapAccumB,
33 takeList, dropList, splitAtList, split,
36 isEqual, eqListBy, equalLength, compareLength,
37 thenCmp, cmpList, prefixMatch, suffixMatch, maybePrefixMatch,
53 -- Floating point stuff
57 createDirectoryHierarchy,
59 modificationTimeIfExists,
61 later, handleDyn, handle,
65 splitFilename, getFileSuffix, splitFilenameDir,
66 splitFilename3, removeSuffix,
67 dropLongestPrefix, takeLongestPrefix, splitLongestPrefix,
68 replaceFilenameSuffix, directoryOf, filenameOf,
69 replaceFilenameDirectory,
70 escapeSpaces, isPathSeparator,
71 normalisePath, platformPath, pgmPath,
74 #include "HsVersions.h"
76 import Panic ( panic, trace )
79 import EXCEPTION ( Exception(..), finally, throwDyn, catchDyn, throw )
80 import qualified EXCEPTION as Exception
81 import DYNAMIC ( Typeable )
82 import DATA_IOREF ( IORef, newIORef )
83 import UNSAFE_IO ( unsafePerformIO )
84 import DATA_IOREF ( readIORef, writeIORef )
86 import qualified List ( elem, notElem )
89 import List ( zipWith4 )
93 import IO ( catch, isDoesNotExistError )
94 import Directory ( doesDirectoryExist, createDirectory )
95 import Char ( isUpper, isAlphaNum, isSpace, ord, isDigit )
97 import Time ( ClockTime )
98 import Directory ( getModificationTime )
103 %************************************************************************
105 \subsection{The Eager monad}
107 %************************************************************************
109 The @Eager@ monad is just an encoding of continuation-passing style,
110 used to allow you to express "do this and then that", mainly to avoid
111 space leaks. It's done with a type synonym to save bureaucracy.
116 type Eager ans a = (a -> ans) -> ans
118 runEager :: Eager a a -> a
119 runEager m = m (\x -> x)
121 appEager :: Eager ans a -> (a -> ans) -> ans
122 appEager m cont = m cont
124 thenEager :: Eager ans a -> (a -> Eager ans b) -> Eager ans b
125 thenEager m k cont = m (\r -> k r cont)
127 returnEager :: a -> Eager ans a
128 returnEager v cont = cont v
130 mapEager :: (a -> Eager ans b) -> [a] -> Eager ans [b]
131 mapEager f [] = returnEager []
132 mapEager f (x:xs) = f x `thenEager` \ y ->
133 mapEager f xs `thenEager` \ ys ->
138 %************************************************************************
140 \subsection{A for loop}
142 %************************************************************************
145 -- Compose a function with itself n times. (nth rather than twice)
146 nTimes :: Int -> (a -> a) -> (a -> a)
149 nTimes n f = f . nTimes (n-1) f
152 %************************************************************************
154 \subsection[Utils-lists]{General list processing}
156 %************************************************************************
159 filterOut :: (a->Bool) -> [a] -> [a]
160 -- Like filter, only reverses the sense of the test
162 filterOut p (x:xs) | p x = filterOut p xs
163 | otherwise = x : filterOut p xs
166 A paranoid @zip@ (and some @zipWith@ friends) that checks the lists
167 are of equal length. Alastair Reid thinks this should only happen if
168 DEBUGging on; hey, why not?
171 zipEqual :: String -> [a] -> [b] -> [(a,b)]
172 zipWithEqual :: String -> (a->b->c) -> [a]->[b]->[c]
173 zipWith3Equal :: String -> (a->b->c->d) -> [a]->[b]->[c]->[d]
174 zipWith4Equal :: String -> (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e]
178 zipWithEqual _ = zipWith
179 zipWith3Equal _ = zipWith3
180 zipWith4Equal _ = zipWith4
182 zipEqual msg [] [] = []
183 zipEqual msg (a:as) (b:bs) = (a,b) : zipEqual msg as bs
184 zipEqual msg as bs = panic ("zipEqual: unequal lists:"++msg)
186 zipWithEqual msg z (a:as) (b:bs)= z a b : zipWithEqual msg z as bs
187 zipWithEqual msg _ [] [] = []
188 zipWithEqual msg _ _ _ = panic ("zipWithEqual: unequal lists:"++msg)
190 zipWith3Equal msg z (a:as) (b:bs) (c:cs)
191 = z a b c : zipWith3Equal msg z as bs cs
192 zipWith3Equal msg _ [] [] [] = []
193 zipWith3Equal msg _ _ _ _ = panic ("zipWith3Equal: unequal lists:"++msg)
195 zipWith4Equal msg z (a:as) (b:bs) (c:cs) (d:ds)
196 = z a b c d : zipWith4Equal msg z as bs cs ds
197 zipWith4Equal msg _ [] [] [] [] = []
198 zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists:"++msg)
203 -- zipLazy is lazy in the second list (observe the ~)
205 zipLazy :: [a] -> [b] -> [(a,b)]
207 zipLazy (x:xs) ~(y:ys) = (x,y) : zipLazy xs ys
212 stretchZipWith :: (a -> Bool) -> b -> (a->b->c) -> [a] -> [b] -> [c]
213 -- (stretchZipWith p z f xs ys) stretches ys by inserting z in
214 -- the places where p returns *True*
216 stretchZipWith p z f [] ys = []
217 stretchZipWith p z f (x:xs) ys
218 | p x = f x z : stretchZipWith p z f xs ys
219 | otherwise = case ys of
221 (y:ys) -> f x y : stretchZipWith p z f xs ys
226 mapAndUnzip :: (a -> (b, c)) -> [a] -> ([b], [c])
228 mapAndUnzip f [] = ([],[])
232 (rs1, rs2) = mapAndUnzip f xs
236 mapAndUnzip3 :: (a -> (b, c, d)) -> [a] -> ([b], [c], [d])
238 mapAndUnzip3 f [] = ([],[],[])
239 mapAndUnzip3 f (x:xs)
242 (rs1, rs2, rs3) = mapAndUnzip3 f xs
244 (r1:rs1, r2:rs2, r3:rs3)
248 nOfThem :: Int -> a -> [a]
249 nOfThem n thing = replicate n thing
251 -- 'atLength atLen atEnd ls n' unravels list 'ls' to position 'n';
254 -- atLength atLenPred atEndPred ls n
255 -- | n < 0 = atLenPred n
256 -- | length ls < n = atEndPred (n - length ls)
257 -- | otherwise = atLenPred (drop n ls)
259 atLength :: ([a] -> b)
264 atLength atLenPred atEndPred ls n
265 | n < 0 = atEndPred n
266 | otherwise = go n ls
268 go n [] = atEndPred n
269 go 0 ls = atLenPred ls
270 go n (_:xs) = go (n-1) xs
273 lengthExceeds :: [a] -> Int -> Bool
274 -- (lengthExceeds xs n) = (length xs > n)
275 lengthExceeds = atLength notNull (const False)
277 lengthAtLeast :: [a] -> Int -> Bool
278 lengthAtLeast = atLength notNull (== 0)
280 lengthIs :: [a] -> Int -> Bool
281 lengthIs = atLength null (==0)
283 listLengthCmp :: [a] -> Int -> Ordering
284 listLengthCmp = atLength atLen atEnd
288 | x > 0 = LT -- not yet seen 'n' elts, so list length is < n.
294 isSingleton :: [a] -> Bool
295 isSingleton [x] = True
296 isSingleton _ = False
298 notNull :: [a] -> Bool
302 snocView :: [a] -> Maybe ([a],a)
303 -- Split off the last element
304 snocView [] = Nothing
305 snocView xs = go [] xs
307 -- Invariant: second arg is non-empty
308 go acc [x] = Just (reverse acc, x)
309 go acc (x:xs) = go (x:acc) xs
319 Debugging/specialising versions of \tr{elem} and \tr{notElem}
322 isIn, isn'tIn :: (Eq a) => String -> a -> [a] -> Bool
325 isIn msg x ys = elem__ x ys
326 isn'tIn msg x ys = notElem__ x ys
328 --these are here to be SPECIALIZEd (automagically)
330 elem__ x (y:ys) = x==y || elem__ x ys
332 notElem__ x [] = True
333 notElem__ x (y:ys) = x /= y && notElem__ x ys
337 = elem (_ILIT 0) x ys
341 | i ># _ILIT 100 = trace ("Over-long elem in " ++ msg) $
343 | otherwise = x == y || elem (i +# _ILIT(1)) x ys
346 = notElem (_ILIT 0) x ys
348 notElem i x [] = True
350 | i ># _ILIT 100 = trace ("Over-long notElem in " ++ msg) $
351 x `List.notElem` (y:ys)
352 | otherwise = x /= y && notElem (i +# _ILIT(1)) x ys
356 %************************************************************************
358 \subsubsection[Utils-Carsten-mergesort]{A mergesort from Carsten}
360 %************************************************************************
363 Date: Mon, 3 May 93 20:45:23 +0200
364 From: Carsten Kehler Holst <kehler@cs.chalmers.se>
365 To: partain@dcs.gla.ac.uk
366 Subject: natural merge sort beats quick sort [ and it is prettier ]
368 Here is a piece of Haskell code that I'm rather fond of. See it as an
369 attempt to get rid of the ridiculous quick-sort routine. group is
370 quite useful by itself I think it was John's idea originally though I
371 believe the lazy version is due to me [surprisingly complicated].
372 gamma [used to be called] is called gamma because I got inspired by
373 the Gamma calculus. It is not very close to the calculus but does
374 behave less sequentially than both foldr and foldl. One could imagine
375 a version of gamma that took a unit element as well thereby avoiding
376 the problem with empty lists.
378 I've tried this code against
380 1) insertion sort - as provided by haskell
381 2) the normal implementation of quick sort
382 3) a deforested version of quick sort due to Jan Sparud
383 4) a super-optimized-quick-sort of Lennart's
385 If the list is partially sorted both merge sort and in particular
386 natural merge sort wins. If the list is random [ average length of
387 rising subsequences = approx 2 ] mergesort still wins and natural
388 merge sort is marginally beaten by Lennart's soqs. The space
389 consumption of merge sort is a bit worse than Lennart's quick sort
390 approx a factor of 2. And a lot worse if Sparud's bug-fix [see his
391 fpca article ] isn't used because of group.
398 group :: (a -> a -> Bool) -> [a] -> [[a]]
399 -- Given a <= function, group finds maximal contiguous up-runs
400 -- or down-runs in the input list.
401 -- It's stable, in the sense that it never re-orders equal elements
403 -- Date: Mon, 12 Feb 1996 15:09:41 +0000
404 -- From: Andy Gill <andy@dcs.gla.ac.uk>
405 -- Here is a `better' definition of group.
408 group p (x:xs) = group' xs x x (x :)
410 group' [] _ _ s = [s []]
411 group' (x:xs) x_min x_max s
412 | x_max `p` x = group' xs x_min x (s . (x :))
413 | not (x_min `p` x) = group' xs x x_max ((x :) . s)
414 | otherwise = s [] : group' xs x x (x :)
415 -- NB: the 'not' is essential for stablity
416 -- x `p` x_min would reverse equal elements
418 generalMerge :: (a -> a -> Bool) -> [a] -> [a] -> [a]
419 generalMerge p xs [] = xs
420 generalMerge p [] ys = ys
421 generalMerge p (x:xs) (y:ys) | x `p` y = x : generalMerge p xs (y:ys)
422 | otherwise = y : generalMerge p (x:xs) ys
424 -- gamma is now called balancedFold
426 balancedFold :: (a -> a -> a) -> [a] -> a
427 balancedFold f [] = error "can't reduce an empty list using balancedFold"
428 balancedFold f [x] = x
429 balancedFold f l = balancedFold f (balancedFold' f l)
431 balancedFold' :: (a -> a -> a) -> [a] -> [a]
432 balancedFold' f (x:y:xs) = f x y : balancedFold' f xs
433 balancedFold' f xs = xs
435 generalNaturalMergeSort p [] = []
436 generalNaturalMergeSort p xs = (balancedFold (generalMerge p) . group p) xs
439 generalMergeSort p [] = []
440 generalMergeSort p xs = (balancedFold (generalMerge p) . map (: [])) xs
442 mergeSort, naturalMergeSort :: Ord a => [a] -> [a]
444 mergeSort = generalMergeSort (<=)
445 naturalMergeSort = generalNaturalMergeSort (<=)
447 mergeSortLe le = generalMergeSort le
450 sortLe :: (a->a->Bool) -> [a] -> [a]
451 sortLe le = generalNaturalMergeSort le
453 sortWith :: Ord b => (a->b) -> [a] -> [a]
454 sortWith get_key xs = sortLe le xs
456 x `le` y = get_key x < get_key y
459 %************************************************************************
461 \subsection[Utils-transitive-closure]{Transitive closure}
463 %************************************************************************
465 This algorithm for transitive closure is straightforward, albeit quadratic.
468 transitiveClosure :: (a -> [a]) -- Successor function
469 -> (a -> a -> Bool) -- Equality predicate
471 -> [a] -- The transitive closure
473 transitiveClosure succ eq xs
477 go done (x:xs) | x `is_in` done = go done xs
478 | otherwise = go (x:done) (succ x ++ xs)
481 x `is_in` (y:ys) | eq x y = True
482 | otherwise = x `is_in` ys
485 %************************************************************************
487 \subsection[Utils-accum]{Accumulating}
489 %************************************************************************
491 @mapAccumL@ behaves like a combination
492 of @map@ and @foldl@;
493 it applies a function to each element of a list, passing an accumulating
494 parameter from left to right, and returning a final value of this
495 accumulator together with the new list.
498 mapAccumL :: (acc -> x -> (acc, y)) -- Function of elt of input list
499 -- and accumulator, returning new
500 -- accumulator and elt of result list
501 -> acc -- Initial accumulator
503 -> (acc, [y]) -- Final accumulator and result list
505 mapAccumL f b [] = (b, [])
506 mapAccumL f b (x:xs) = (b'', x':xs') where
508 (b'', xs') = mapAccumL f b' xs
511 @mapAccumR@ does the same, but working from right to left instead. Its type is
512 the same as @mapAccumL@, though.
515 mapAccumR :: (acc -> x -> (acc, y)) -- Function of elt of input list
516 -- and accumulator, returning new
517 -- accumulator and elt of result list
518 -> acc -- Initial accumulator
520 -> (acc, [y]) -- Final accumulator and result list
522 mapAccumR f b [] = (b, [])
523 mapAccumR f b (x:xs) = (b'', x':xs') where
525 (b', xs') = mapAccumR f b xs
528 Here is the bi-directional version, that works from both left and right.
531 mapAccumB :: (accl -> accr -> x -> (accl, accr,y))
532 -- Function of elt of input list
533 -- and accumulator, returning new
534 -- accumulator and elt of result list
535 -> accl -- Initial accumulator from left
536 -> accr -- Initial accumulator from right
538 -> (accl, accr, [y]) -- Final accumulators and result list
540 mapAccumB f a b [] = (a,b,[])
541 mapAccumB f a b (x:xs) = (a'',b'',y:ys)
543 (a',b'',y) = f a b' x
544 (a'',b',ys) = mapAccumB f a' b xs
547 A strict version of foldl.
550 foldl' :: (a -> b -> a) -> a -> [b] -> a
551 foldl' f z xs = lgo z xs
554 lgo z (x:xs) = (lgo $! (f z x)) xs
557 A combination of foldl with zip. It works with equal length lists.
560 foldl2 :: (acc -> a -> b -> acc) -> acc -> [a] -> [b] -> acc
562 foldl2 k z (a:as) (b:bs) = foldl2 k (k z a b) as bs
565 Count the number of times a predicate is true
568 count :: (a -> Bool) -> [a] -> Int
570 count p (x:xs) | p x = 1 + count p xs
571 | otherwise = count p xs
574 @splitAt@, @take@, and @drop@ but with length of another
575 list giving the break-off point:
578 takeList :: [b] -> [a] -> [a]
583 (y:ys) -> y : takeList xs ys
585 dropList :: [b] -> [a] -> [a]
587 dropList _ xs@[] = xs
588 dropList (_:xs) (_:ys) = dropList xs ys
591 splitAtList :: [b] -> [a] -> ([a], [a])
592 splitAtList [] xs = ([], xs)
593 splitAtList _ xs@[] = (xs, xs)
594 splitAtList (_:xs) (y:ys) = (y:ys', ys'')
596 (ys', ys'') = splitAtList xs ys
598 split :: Char -> String -> [String]
599 split c s = case rest of
601 _:rest -> chunk : split c rest
602 where (chunk, rest) = break (==c) s
606 %************************************************************************
608 \subsection[Utils-comparison]{Comparisons}
610 %************************************************************************
613 isEqual :: Ordering -> Bool
614 -- Often used in (isEqual (a `compare` b))
619 thenCmp :: Ordering -> Ordering -> Ordering
620 {-# INLINE thenCmp #-}
622 thenCmp other any = other
624 eqListBy :: (a->a->Bool) -> [a] -> [a] -> Bool
625 eqListBy eq [] [] = True
626 eqListBy eq (x:xs) (y:ys) = eq x y && eqListBy eq xs ys
627 eqListBy eq xs ys = False
629 equalLength :: [a] -> [b] -> Bool
630 equalLength [] [] = True
631 equalLength (_:xs) (_:ys) = equalLength xs ys
632 equalLength xs ys = False
634 compareLength :: [a] -> [b] -> Ordering
635 compareLength [] [] = EQ
636 compareLength (_:xs) (_:ys) = compareLength xs ys
637 compareLength [] _ys = LT
638 compareLength _xs [] = GT
640 cmpList :: (a -> a -> Ordering) -> [a] -> [a] -> Ordering
641 -- `cmpList' uses a user-specified comparer
643 cmpList cmp [] [] = EQ
644 cmpList cmp [] _ = LT
645 cmpList cmp _ [] = GT
646 cmpList cmp (a:as) (b:bs)
647 = case cmp a b of { EQ -> cmpList cmp as bs; xxx -> xxx }
651 prefixMatch :: Eq a => [a] -> [a] -> Bool
652 prefixMatch [] _str = True
653 prefixMatch _pat [] = False
654 prefixMatch (p:ps) (s:ss) | p == s = prefixMatch ps ss
657 maybePrefixMatch :: String -> String -> Maybe String
658 maybePrefixMatch [] rest = Just rest
659 maybePrefixMatch (_:_) [] = Nothing
660 maybePrefixMatch (p:pat) (r:rest)
661 | p == r = maybePrefixMatch pat rest
662 | otherwise = Nothing
664 suffixMatch :: Eq a => [a] -> [a] -> Bool
665 suffixMatch pat str = prefixMatch (reverse pat) (reverse str)
667 removeSpaces :: String -> String
668 removeSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace
671 %************************************************************************
673 \subsection[Utils-pairs]{Pairs}
675 %************************************************************************
677 The following are curried versions of @fst@ and @snd@.
681 cfst :: a -> b -> a -- stranal-sem only (Note)
686 The following provide us higher order functions that, when applied
687 to a function, operate on pairs.
691 applyToPair :: ((a -> c),(b -> d)) -> (a,b) -> (c,d)
692 applyToPair (f,g) (x,y) = (f x, g y)
694 applyToFst :: (a -> c) -> (a,b)-> (c,b)
695 applyToFst f (x,y) = (f x,y)
697 applyToSnd :: (b -> d) -> (a,b) -> (a,d)
698 applyToSnd f (x,y) = (x,f y)
703 unzipWith :: (a -> b -> c) -> [(a, b)] -> [c]
704 unzipWith f pairs = map ( \ (a, b) -> f a b ) pairs
708 seqList :: [a] -> b -> b
710 seqList (x:xs) b = x `seq` seqList xs b
716 global :: a -> IORef a
717 global a = unsafePerformIO (newIORef a)
721 consIORef :: IORef [a] -> a -> IO ()
724 writeIORef var (x:xs)
730 looksLikeModuleName [] = False
731 looksLikeModuleName (c:cs) = isUpper c && go cs
733 go ('.':cs) = looksLikeModuleName cs
734 go (c:cs) = (isAlphaNum c || c == '_') && go cs
737 Akin to @Prelude.words@, but sensitive to dquoted entities treating
738 them as single words.
741 toArgs :: String -> [String]
744 case break (\ ch -> isSpace ch || ch == '"') (dropWhile isSpace s) of -- "
746 (\ ws -> if null w then ws else w : ws) $
750 | x /= '"' -> toArgs xs
753 ((str,rs):_) -> stripQuotes str : toArgs rs
756 -- strip away dquotes; assume first and last chars contain quotes.
757 stripQuotes :: String -> String
758 stripQuotes ('"':xs) = init xs
762 -- -----------------------------------------------------------------------------
766 readRational__ :: ReadS Rational -- NB: doesn't handle leading "-"
767 readRational__ r = do
770 return ((n%1)*10^^(k-d), t)
773 (ds,s) <- lexDecDigits r
774 (ds',t) <- lexDotDigits s
775 return (read (ds++ds'), length ds', t)
777 readExp (e:s) | e `elem` "eE" = readExp' s
778 readExp s = return (0,s)
780 readExp' ('+':s) = readDec s
781 readExp' ('-':s) = do
784 readExp' s = readDec s
787 (ds,r) <- nonnull isDigit s
788 return (foldl1 (\n d -> n * 10 + d) [ ord d - ord '0' | d <- ds ],
791 lexDecDigits = nonnull isDigit
793 lexDotDigits ('.':s) = return (span isDigit s)
794 lexDotDigits s = return ("",s)
796 nonnull p s = do (cs@(_:_),t) <- return (span p s)
799 readRational :: String -> Rational -- NB: *does* handle a leading "-"
802 '-' : xs -> - (read_me xs)
806 = case (do { (x,"") <- readRational__ s ; return x }) of
808 [] -> error ("readRational: no parse:" ++ top_s)
809 _ -> error ("readRational: ambiguous parse:" ++ top_s)
812 -----------------------------------------------------------------------------
813 -- Create a hierarchy of directories
815 createDirectoryHierarchy :: FilePath -> IO ()
816 createDirectoryHierarchy dir = do
817 b <- doesDirectoryExist dir
819 createDirectoryHierarchy (directoryOf dir)
822 -----------------------------------------------------------------------------
823 -- Verify that the 'dirname' portion of a FilePath exists.
825 doesDirNameExist :: FilePath -> IO Bool
826 doesDirNameExist fpath = doesDirectoryExist (directoryOf fpath)
828 -- -----------------------------------------------------------------------------
833 handleDyn :: Typeable ex => (ex -> IO a) -> IO a -> IO a
834 handleDyn = flip catchDyn
836 handle :: (Exception -> IO a) -> IO a -> IO a
837 #if __GLASGOW_HASKELL__ < 501
838 handle = flip Exception.catchAllIO
840 handle h f = f `Exception.catch` \e -> case e of
841 ExitException _ -> throw e
845 -- --------------------------------------------------------------
846 -- check existence & modification time at the same time
848 modificationTimeIfExists :: FilePath -> IO (Maybe ClockTime)
849 modificationTimeIfExists f = do
850 (do t <- getModificationTime f; return (Just t))
851 `IO.catch` \e -> if isDoesNotExistError e
855 -- --------------------------------------------------------------
856 -- Filename manipulation
860 splitFilename :: String -> (String,Suffix)
861 splitFilename f = splitLongestPrefix f (=='.')
863 getFileSuffix :: String -> Suffix
864 getFileSuffix f = dropLongestPrefix f (=='.')
866 -- "foo/bar/xyzzy.ext" -> ("foo/bar", "xyzzy.ext")
867 splitFilenameDir :: String -> (String,String)
869 = let (dir, rest) = splitLongestPrefix str isPathSeparator
870 real_dir | null dir = "."
874 -- "foo/bar/xyzzy.ext" -> ("foo/bar", "xyzzy", ".ext")
875 splitFilename3 :: String -> (String,String,Suffix)
877 = let (dir, rest) = splitLongestPrefix str isPathSeparator
878 (name, ext) = splitFilename rest
879 real_dir | null dir = "."
881 in (real_dir, name, ext)
883 removeSuffix :: Char -> String -> Suffix
886 | otherwise = reverse pre
887 where (suf,pre) = break (==c) (reverse s)
889 dropLongestPrefix :: String -> (Char -> Bool) -> String
890 dropLongestPrefix s pred = reverse suf
891 where (suf,_pre) = break pred (reverse s)
893 takeLongestPrefix :: String -> (Char -> Bool) -> String
894 takeLongestPrefix s pred = reverse pre
895 where (_suf,pre) = break pred (reverse s)
897 -- split a string at the last character where 'pred' is True,
898 -- returning a pair of strings. The first component holds the string
899 -- up (but not including) the last character for which 'pred' returned
900 -- True, the second whatever comes after (but also not including the
903 -- If 'pred' returns False for all characters in the string, the original
904 -- string is returned in the second component (and the first one is just
906 splitLongestPrefix :: String -> (Char -> Bool) -> (String,String)
907 splitLongestPrefix s pred
909 [] -> ([], reverse suf)
910 (_:pre) -> (reverse pre, reverse suf)
911 where (suf,pre) = break pred (reverse s)
913 replaceFilenameSuffix :: FilePath -> Suffix -> FilePath
914 replaceFilenameSuffix s suf = removeSuffix '.' s ++ suf
916 -- directoryOf strips the filename off the input string, returning
918 directoryOf :: FilePath -> String
919 directoryOf = fst . splitFilenameDir
921 -- filenameOf strips the directory off the input string, returning
923 filenameOf :: FilePath -> String
924 filenameOf = snd . splitFilenameDir
926 replaceFilenameDirectory :: FilePath -> String -> FilePath
927 replaceFilenameDirectory s dir
928 = dir ++ '/':dropLongestPrefix s isPathSeparator
930 escapeSpaces :: String -> String
931 escapeSpaces = foldr (\c s -> if isSpace c then '\\':c:s else c:s) ""
933 isPathSeparator :: Char -> Bool
935 #ifdef mingw32_TARGET_OS
936 ch == '/' || ch == '\\'
941 -----------------------------------------------------------------------------
942 -- Convert filepath into platform / MSDOS form.
944 -- We maintain path names in Unix form ('/'-separated) right until
945 -- the last moment. On Windows we dos-ify them just before passing them
946 -- to the Windows command.
948 -- The alternative, of using '/' consistently on Unix and '\' on Windows,
949 -- proved quite awkward. There were a lot more calls to platformPath,
950 -- and even on Windows we might invoke a unix-like utility (eg 'sh'), which
951 -- interpreted a command line 'foo\baz' as 'foobaz'.
953 normalisePath :: String -> String
954 -- Just changes '\' to '/'
956 pgmPath :: String -- Directory string in Unix format
957 -> String -- Program name with no directory separators
959 -> String -- Program invocation string in native format
961 #if defined(mingw32_HOST_OS)
962 --------------------- Windows version ------------------
963 normalisePath xs = subst '\\' '/' xs
964 pgmPath dir pgm = platformPath dir ++ '\\' : pgm
965 platformPath p = subst '/' '\\' p
967 subst a b ls = map (\ x -> if x == a then b else x) ls
969 --------------------- Non-Windows version --------------
970 normalisePath xs = xs
971 pgmPath dir pgm = dir ++ '/' : pgm
972 platformPath stuff = stuff
973 --------------------------------------------------------