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,
60 later, handleDyn, handle,
64 splitFilename, getFileSuffix, splitFilenameDir,
65 splitFilename3, removeSuffix,
66 dropLongestPrefix, takeLongestPrefix, splitLongestPrefix,
67 replaceFilenameSuffix, directoryOf, filenameOf,
68 replaceFilenameDirectory,
69 escapeSpaces, isPathSeparator,
70 normalisePath, platformPath, pgmPath,
73 #include "HsVersions.h"
75 import Panic ( panic, trace )
78 import EXCEPTION ( Exception(..), finally, throwDyn, catchDyn, throw )
79 import qualified EXCEPTION as Exception
80 import DYNAMIC ( Typeable )
81 import DATA_IOREF ( IORef, newIORef )
82 import UNSAFE_IO ( unsafePerformIO )
83 import DATA_IOREF ( readIORef, writeIORef )
85 import qualified List ( elem, notElem )
88 import List ( zipWith4 )
93 import Directory ( doesDirectoryExist, createDirectory )
94 import Char ( isUpper, isAlphaNum, isSpace, ord, isDigit )
100 %************************************************************************
102 \subsection{The Eager monad}
104 %************************************************************************
106 The @Eager@ monad is just an encoding of continuation-passing style,
107 used to allow you to express "do this and then that", mainly to avoid
108 space leaks. It's done with a type synonym to save bureaucracy.
113 type Eager ans a = (a -> ans) -> ans
115 runEager :: Eager a a -> a
116 runEager m = m (\x -> x)
118 appEager :: Eager ans a -> (a -> ans) -> ans
119 appEager m cont = m cont
121 thenEager :: Eager ans a -> (a -> Eager ans b) -> Eager ans b
122 thenEager m k cont = m (\r -> k r cont)
124 returnEager :: a -> Eager ans a
125 returnEager v cont = cont v
127 mapEager :: (a -> Eager ans b) -> [a] -> Eager ans [b]
128 mapEager f [] = returnEager []
129 mapEager f (x:xs) = f x `thenEager` \ y ->
130 mapEager f xs `thenEager` \ ys ->
135 %************************************************************************
137 \subsection{A for loop}
139 %************************************************************************
142 -- Compose a function with itself n times. (nth rather than twice)
143 nTimes :: Int -> (a -> a) -> (a -> a)
146 nTimes n f = f . nTimes (n-1) f
149 %************************************************************************
151 \subsection[Utils-lists]{General list processing}
153 %************************************************************************
156 filterOut :: (a->Bool) -> [a] -> [a]
157 -- Like filter, only reverses the sense of the test
159 filterOut p (x:xs) | p x = filterOut p xs
160 | otherwise = x : filterOut p xs
163 A paranoid @zip@ (and some @zipWith@ friends) that checks the lists
164 are of equal length. Alastair Reid thinks this should only happen if
165 DEBUGging on; hey, why not?
168 zipEqual :: String -> [a] -> [b] -> [(a,b)]
169 zipWithEqual :: String -> (a->b->c) -> [a]->[b]->[c]
170 zipWith3Equal :: String -> (a->b->c->d) -> [a]->[b]->[c]->[d]
171 zipWith4Equal :: String -> (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e]
175 zipWithEqual _ = zipWith
176 zipWith3Equal _ = zipWith3
177 zipWith4Equal _ = zipWith4
179 zipEqual msg [] [] = []
180 zipEqual msg (a:as) (b:bs) = (a,b) : zipEqual msg as bs
181 zipEqual msg as bs = panic ("zipEqual: unequal lists:"++msg)
183 zipWithEqual msg z (a:as) (b:bs)= z a b : zipWithEqual msg z as bs
184 zipWithEqual msg _ [] [] = []
185 zipWithEqual msg _ _ _ = panic ("zipWithEqual: unequal lists:"++msg)
187 zipWith3Equal msg z (a:as) (b:bs) (c:cs)
188 = z a b c : zipWith3Equal msg z as bs cs
189 zipWith3Equal msg _ [] [] [] = []
190 zipWith3Equal msg _ _ _ _ = panic ("zipWith3Equal: unequal lists:"++msg)
192 zipWith4Equal msg z (a:as) (b:bs) (c:cs) (d:ds)
193 = z a b c d : zipWith4Equal msg z as bs cs ds
194 zipWith4Equal msg _ [] [] [] [] = []
195 zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists:"++msg)
200 -- zipLazy is lazy in the second list (observe the ~)
202 zipLazy :: [a] -> [b] -> [(a,b)]
204 zipLazy (x:xs) ~(y:ys) = (x,y) : zipLazy xs ys
209 stretchZipWith :: (a -> Bool) -> b -> (a->b->c) -> [a] -> [b] -> [c]
210 -- (stretchZipWith p z f xs ys) stretches ys by inserting z in
211 -- the places where p returns *True*
213 stretchZipWith p z f [] ys = []
214 stretchZipWith p z f (x:xs) ys
215 | p x = f x z : stretchZipWith p z f xs ys
216 | otherwise = case ys of
218 (y:ys) -> f x y : stretchZipWith p z f xs ys
223 mapAndUnzip :: (a -> (b, c)) -> [a] -> ([b], [c])
225 mapAndUnzip f [] = ([],[])
229 (rs1, rs2) = mapAndUnzip f xs
233 mapAndUnzip3 :: (a -> (b, c, d)) -> [a] -> ([b], [c], [d])
235 mapAndUnzip3 f [] = ([],[],[])
236 mapAndUnzip3 f (x:xs)
239 (rs1, rs2, rs3) = mapAndUnzip3 f xs
241 (r1:rs1, r2:rs2, r3:rs3)
245 nOfThem :: Int -> a -> [a]
246 nOfThem n thing = replicate n thing
248 -- 'atLength atLen atEnd ls n' unravels list 'ls' to position 'n';
251 -- atLength atLenPred atEndPred ls n
252 -- | n < 0 = atLenPred n
253 -- | length ls < n = atEndPred (n - length ls)
254 -- | otherwise = atLenPred (drop n ls)
256 atLength :: ([a] -> b)
261 atLength atLenPred atEndPred ls n
262 | n < 0 = atEndPred n
263 | otherwise = go n ls
265 go n [] = atEndPred n
266 go 0 ls = atLenPred ls
267 go n (_:xs) = go (n-1) xs
270 lengthExceeds :: [a] -> Int -> Bool
271 -- (lengthExceeds xs n) = (length xs > n)
272 lengthExceeds = atLength notNull (const False)
274 lengthAtLeast :: [a] -> Int -> Bool
275 lengthAtLeast = atLength notNull (== 0)
277 lengthIs :: [a] -> Int -> Bool
278 lengthIs = atLength null (==0)
280 listLengthCmp :: [a] -> Int -> Ordering
281 listLengthCmp = atLength atLen atEnd
285 | x > 0 = LT -- not yet seen 'n' elts, so list length is < n.
291 isSingleton :: [a] -> Bool
292 isSingleton [x] = True
293 isSingleton _ = False
295 notNull :: [a] -> Bool
299 snocView :: [a] -> Maybe ([a],a)
300 -- Split off the last element
301 snocView [] = Nothing
302 snocView xs = go [] xs
304 -- Invariant: second arg is non-empty
305 go acc [x] = Just (reverse acc, x)
306 go acc (x:xs) = go (x:acc) xs
316 Debugging/specialising versions of \tr{elem} and \tr{notElem}
319 isIn, isn'tIn :: (Eq a) => String -> a -> [a] -> Bool
322 isIn msg x ys = elem__ x ys
323 isn'tIn msg x ys = notElem__ x ys
325 --these are here to be SPECIALIZEd (automagically)
327 elem__ x (y:ys) = x==y || elem__ x ys
329 notElem__ x [] = True
330 notElem__ x (y:ys) = x /= y && notElem__ x ys
334 = elem (_ILIT 0) x ys
338 | i ># _ILIT 100 = trace ("Over-long elem in " ++ msg) $
340 | otherwise = x == y || elem (i +# _ILIT(1)) x ys
343 = notElem (_ILIT 0) x ys
345 notElem i x [] = True
347 | i ># _ILIT 100 = trace ("Over-long notElem in " ++ msg) $
348 x `List.notElem` (y:ys)
349 | otherwise = x /= y && notElem (i +# _ILIT(1)) x ys
353 %************************************************************************
355 \subsubsection[Utils-Carsten-mergesort]{A mergesort from Carsten}
357 %************************************************************************
360 Date: Mon, 3 May 93 20:45:23 +0200
361 From: Carsten Kehler Holst <kehler@cs.chalmers.se>
362 To: partain@dcs.gla.ac.uk
363 Subject: natural merge sort beats quick sort [ and it is prettier ]
365 Here is a piece of Haskell code that I'm rather fond of. See it as an
366 attempt to get rid of the ridiculous quick-sort routine. group is
367 quite useful by itself I think it was John's idea originally though I
368 believe the lazy version is due to me [surprisingly complicated].
369 gamma [used to be called] is called gamma because I got inspired by
370 the Gamma calculus. It is not very close to the calculus but does
371 behave less sequentially than both foldr and foldl. One could imagine
372 a version of gamma that took a unit element as well thereby avoiding
373 the problem with empty lists.
375 I've tried this code against
377 1) insertion sort - as provided by haskell
378 2) the normal implementation of quick sort
379 3) a deforested version of quick sort due to Jan Sparud
380 4) a super-optimized-quick-sort of Lennart's
382 If the list is partially sorted both merge sort and in particular
383 natural merge sort wins. If the list is random [ average length of
384 rising subsequences = approx 2 ] mergesort still wins and natural
385 merge sort is marginally beaten by Lennart's soqs. The space
386 consumption of merge sort is a bit worse than Lennart's quick sort
387 approx a factor of 2. And a lot worse if Sparud's bug-fix [see his
388 fpca article ] isn't used because of group.
395 group :: (a -> a -> Bool) -> [a] -> [[a]]
396 -- Given a <= function, group finds maximal contiguous up-runs
397 -- or down-runs in the input list.
398 -- It's stable, in the sense that it never re-orders equal elements
400 -- Date: Mon, 12 Feb 1996 15:09:41 +0000
401 -- From: Andy Gill <andy@dcs.gla.ac.uk>
402 -- Here is a `better' definition of group.
405 group p (x:xs) = group' xs x x (x :)
407 group' [] _ _ s = [s []]
408 group' (x:xs) x_min x_max s
409 | x_max `p` x = group' xs x_min x (s . (x :))
410 | not (x_min `p` x) = group' xs x x_max ((x :) . s)
411 | otherwise = s [] : group' xs x x (x :)
412 -- NB: the 'not' is essential for stablity
413 -- x `p` x_min would reverse equal elements
415 generalMerge :: (a -> a -> Bool) -> [a] -> [a] -> [a]
416 generalMerge p xs [] = xs
417 generalMerge p [] ys = ys
418 generalMerge p (x:xs) (y:ys) | x `p` y = x : generalMerge p xs (y:ys)
419 | otherwise = y : generalMerge p (x:xs) ys
421 -- gamma is now called balancedFold
423 balancedFold :: (a -> a -> a) -> [a] -> a
424 balancedFold f [] = error "can't reduce an empty list using balancedFold"
425 balancedFold f [x] = x
426 balancedFold f l = balancedFold f (balancedFold' f l)
428 balancedFold' :: (a -> a -> a) -> [a] -> [a]
429 balancedFold' f (x:y:xs) = f x y : balancedFold' f xs
430 balancedFold' f xs = xs
432 generalNaturalMergeSort p [] = []
433 generalNaturalMergeSort p xs = (balancedFold (generalMerge p) . group p) xs
436 generalMergeSort p [] = []
437 generalMergeSort p xs = (balancedFold (generalMerge p) . map (: [])) xs
439 mergeSort, naturalMergeSort :: Ord a => [a] -> [a]
441 mergeSort = generalMergeSort (<=)
442 naturalMergeSort = generalNaturalMergeSort (<=)
444 mergeSortLe le = generalMergeSort le
447 sortLe :: (a->a->Bool) -> [a] -> [a]
448 sortLe le = generalNaturalMergeSort le
450 sortWith :: Ord b => (a->b) -> [a] -> [a]
451 sortWith get_key xs = sortLe le xs
453 x `le` y = get_key x < get_key y
456 %************************************************************************
458 \subsection[Utils-transitive-closure]{Transitive closure}
460 %************************************************************************
462 This algorithm for transitive closure is straightforward, albeit quadratic.
465 transitiveClosure :: (a -> [a]) -- Successor function
466 -> (a -> a -> Bool) -- Equality predicate
468 -> [a] -- The transitive closure
470 transitiveClosure succ eq xs
474 go done (x:xs) | x `is_in` done = go done xs
475 | otherwise = go (x:done) (succ x ++ xs)
478 x `is_in` (y:ys) | eq x y = True
479 | otherwise = x `is_in` ys
482 %************************************************************************
484 \subsection[Utils-accum]{Accumulating}
486 %************************************************************************
488 @mapAccumL@ behaves like a combination
489 of @map@ and @foldl@;
490 it applies a function to each element of a list, passing an accumulating
491 parameter from left to right, and returning a final value of this
492 accumulator together with the new list.
495 mapAccumL :: (acc -> x -> (acc, y)) -- Function of elt of input list
496 -- and accumulator, returning new
497 -- accumulator and elt of result list
498 -> acc -- Initial accumulator
500 -> (acc, [y]) -- Final accumulator and result list
502 mapAccumL f b [] = (b, [])
503 mapAccumL f b (x:xs) = (b'', x':xs') where
505 (b'', xs') = mapAccumL f b' xs
508 @mapAccumR@ does the same, but working from right to left instead. Its type is
509 the same as @mapAccumL@, though.
512 mapAccumR :: (acc -> x -> (acc, y)) -- Function of elt of input list
513 -- and accumulator, returning new
514 -- accumulator and elt of result list
515 -> acc -- Initial accumulator
517 -> (acc, [y]) -- Final accumulator and result list
519 mapAccumR f b [] = (b, [])
520 mapAccumR f b (x:xs) = (b'', x':xs') where
522 (b', xs') = mapAccumR f b xs
525 Here is the bi-directional version, that works from both left and right.
528 mapAccumB :: (accl -> accr -> x -> (accl, accr,y))
529 -- Function of elt of input list
530 -- and accumulator, returning new
531 -- accumulator and elt of result list
532 -> accl -- Initial accumulator from left
533 -> accr -- Initial accumulator from right
535 -> (accl, accr, [y]) -- Final accumulators and result list
537 mapAccumB f a b [] = (a,b,[])
538 mapAccumB f a b (x:xs) = (a'',b'',y:ys)
540 (a',b'',y) = f a b' x
541 (a'',b',ys) = mapAccumB f a' b xs
544 A strict version of foldl.
547 foldl' :: (a -> b -> a) -> a -> [b] -> a
548 foldl' f z xs = lgo z xs
551 lgo z (x:xs) = (lgo $! (f z x)) xs
554 A combination of foldl with zip. It works with equal length lists.
557 foldl2 :: (acc -> a -> b -> acc) -> acc -> [a] -> [b] -> acc
559 foldl2 k z (a:as) (b:bs) = foldl2 k (k z a b) as bs
562 Count the number of times a predicate is true
565 count :: (a -> Bool) -> [a] -> Int
567 count p (x:xs) | p x = 1 + count p xs
568 | otherwise = count p xs
571 @splitAt@, @take@, and @drop@ but with length of another
572 list giving the break-off point:
575 takeList :: [b] -> [a] -> [a]
580 (y:ys) -> y : takeList xs ys
582 dropList :: [b] -> [a] -> [a]
584 dropList _ xs@[] = xs
585 dropList (_:xs) (_:ys) = dropList xs ys
588 splitAtList :: [b] -> [a] -> ([a], [a])
589 splitAtList [] xs = ([], xs)
590 splitAtList _ xs@[] = (xs, xs)
591 splitAtList (_:xs) (y:ys) = (y:ys', ys'')
593 (ys', ys'') = splitAtList xs ys
595 split :: Char -> String -> [String]
596 split c s = case rest of
598 _:rest -> chunk : split c rest
599 where (chunk, rest) = break (==c) s
603 %************************************************************************
605 \subsection[Utils-comparison]{Comparisons}
607 %************************************************************************
610 isEqual :: Ordering -> Bool
611 -- Often used in (isEqual (a `compare` b))
616 thenCmp :: Ordering -> Ordering -> Ordering
617 {-# INLINE thenCmp #-}
619 thenCmp other any = other
621 eqListBy :: (a->a->Bool) -> [a] -> [a] -> Bool
622 eqListBy eq [] [] = True
623 eqListBy eq (x:xs) (y:ys) = eq x y && eqListBy eq xs ys
624 eqListBy eq xs ys = False
626 equalLength :: [a] -> [b] -> Bool
627 equalLength [] [] = True
628 equalLength (_:xs) (_:ys) = equalLength xs ys
629 equalLength xs ys = False
631 compareLength :: [a] -> [b] -> Ordering
632 compareLength [] [] = EQ
633 compareLength (_:xs) (_:ys) = compareLength xs ys
634 compareLength [] _ys = LT
635 compareLength _xs [] = GT
637 cmpList :: (a -> a -> Ordering) -> [a] -> [a] -> Ordering
638 -- `cmpList' uses a user-specified comparer
640 cmpList cmp [] [] = EQ
641 cmpList cmp [] _ = LT
642 cmpList cmp _ [] = GT
643 cmpList cmp (a:as) (b:bs)
644 = case cmp a b of { EQ -> cmpList cmp as bs; xxx -> xxx }
648 prefixMatch :: Eq a => [a] -> [a] -> Bool
649 prefixMatch [] _str = True
650 prefixMatch _pat [] = False
651 prefixMatch (p:ps) (s:ss) | p == s = prefixMatch ps ss
654 maybePrefixMatch :: String -> String -> Maybe String
655 maybePrefixMatch [] rest = Just rest
656 maybePrefixMatch (_:_) [] = Nothing
657 maybePrefixMatch (p:pat) (r:rest)
658 | p == r = maybePrefixMatch pat rest
659 | otherwise = Nothing
661 suffixMatch :: Eq a => [a] -> [a] -> Bool
662 suffixMatch pat str = prefixMatch (reverse pat) (reverse str)
664 removeSpaces :: String -> String
665 removeSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace
668 %************************************************************************
670 \subsection[Utils-pairs]{Pairs}
672 %************************************************************************
674 The following are curried versions of @fst@ and @snd@.
678 cfst :: a -> b -> a -- stranal-sem only (Note)
683 The following provide us higher order functions that, when applied
684 to a function, operate on pairs.
688 applyToPair :: ((a -> c),(b -> d)) -> (a,b) -> (c,d)
689 applyToPair (f,g) (x,y) = (f x, g y)
691 applyToFst :: (a -> c) -> (a,b)-> (c,b)
692 applyToFst f (x,y) = (f x,y)
694 applyToSnd :: (b -> d) -> (a,b) -> (a,d)
695 applyToSnd f (x,y) = (x,f y)
700 unzipWith :: (a -> b -> c) -> [(a, b)] -> [c]
701 unzipWith f pairs = map ( \ (a, b) -> f a b ) pairs
705 seqList :: [a] -> b -> b
707 seqList (x:xs) b = x `seq` seqList xs b
713 global :: a -> IORef a
714 global a = unsafePerformIO (newIORef a)
718 consIORef :: IORef [a] -> a -> IO ()
721 writeIORef var (x:xs)
727 looksLikeModuleName [] = False
728 looksLikeModuleName (c:cs) = isUpper c && go cs
730 go ('.':cs) = looksLikeModuleName cs
731 go (c:cs) = (isAlphaNum c || c == '_') && go cs
734 Akin to @Prelude.words@, but sensitive to dquoted entities treating
735 them as single words.
738 toArgs :: String -> [String]
741 case break (\ ch -> isSpace ch || ch == '"') (dropWhile isSpace s) of -- "
743 (\ ws -> if null w then ws else w : ws) $
747 | x /= '"' -> toArgs xs
750 ((str,rs):_) -> stripQuotes str : toArgs rs
753 -- strip away dquotes; assume first and last chars contain quotes.
754 stripQuotes :: String -> String
755 stripQuotes ('"':xs) = init xs
759 -- -----------------------------------------------------------------------------
763 readRational__ :: ReadS Rational -- NB: doesn't handle leading "-"
764 readRational__ r = do
767 return ((n%1)*10^^(k-d), t)
770 (ds,s) <- lexDecDigits r
771 (ds',t) <- lexDotDigits s
772 return (read (ds++ds'), length ds', t)
774 readExp (e:s) | e `elem` "eE" = readExp' s
775 readExp s = return (0,s)
777 readExp' ('+':s) = readDec s
778 readExp' ('-':s) = do
781 readExp' s = readDec s
784 (ds,r) <- nonnull isDigit s
785 return (foldl1 (\n d -> n * 10 + d) [ ord d - ord '0' | d <- ds ],
788 lexDecDigits = nonnull isDigit
790 lexDotDigits ('.':s) = return (span isDigit s)
791 lexDotDigits s = return ("",s)
793 nonnull p s = do (cs@(_:_),t) <- return (span p s)
796 readRational :: String -> Rational -- NB: *does* handle a leading "-"
799 '-' : xs -> - (read_me xs)
803 = case (do { (x,"") <- readRational__ s ; return x }) of
805 [] -> error ("readRational: no parse:" ++ top_s)
806 _ -> error ("readRational: ambiguous parse:" ++ top_s)
809 -----------------------------------------------------------------------------
810 -- Create a hierarchy of directories
812 createDirectoryHierarchy :: FilePath -> IO ()
813 createDirectoryHierarchy dir = do
814 b <- doesDirectoryExist dir
816 createDirectoryHierarchy (directoryOf dir)
819 -----------------------------------------------------------------------------
820 -- Verify that the 'dirname' portion of a FilePath exists.
822 doesDirNameExist :: FilePath -> IO Bool
823 doesDirNameExist fpath = doesDirectoryExist (directoryOf fpath)
825 -- -----------------------------------------------------------------------------
830 handleDyn :: Typeable ex => (ex -> IO a) -> IO a -> IO a
831 handleDyn = flip catchDyn
833 handle :: (Exception -> IO a) -> IO a -> IO a
834 #if __GLASGOW_HASKELL__ < 501
835 handle = flip Exception.catchAllIO
837 handle h f = f `Exception.catch` \e -> case e of
838 ExitException _ -> throw e
842 -- --------------------------------------------------------------
843 -- Filename manipulation
847 splitFilename :: String -> (String,Suffix)
848 splitFilename f = splitLongestPrefix f (=='.')
850 getFileSuffix :: String -> Suffix
851 getFileSuffix f = dropLongestPrefix f (=='.')
853 -- "foo/bar/xyzzy.ext" -> ("foo/bar", "xyzzy.ext")
854 splitFilenameDir :: String -> (String,String)
856 = let (dir, rest) = splitLongestPrefix str isPathSeparator
857 real_dir | null dir = "."
861 -- "foo/bar/xyzzy.ext" -> ("foo/bar", "xyzzy", ".ext")
862 splitFilename3 :: String -> (String,String,Suffix)
864 = let (dir, rest) = splitLongestPrefix str isPathSeparator
865 (name, ext) = splitFilename rest
866 real_dir | null dir = "."
868 in (real_dir, name, ext)
870 removeSuffix :: Char -> String -> Suffix
873 | otherwise = reverse pre
874 where (suf,pre) = break (==c) (reverse s)
876 dropLongestPrefix :: String -> (Char -> Bool) -> String
877 dropLongestPrefix s pred = reverse suf
878 where (suf,_pre) = break pred (reverse s)
880 takeLongestPrefix :: String -> (Char -> Bool) -> String
881 takeLongestPrefix s pred = reverse pre
882 where (_suf,pre) = break pred (reverse s)
884 -- split a string at the last character where 'pred' is True,
885 -- returning a pair of strings. The first component holds the string
886 -- up (but not including) the last character for which 'pred' returned
887 -- True, the second whatever comes after (but also not including the
890 -- If 'pred' returns False for all characters in the string, the original
891 -- string is returned in the second component (and the first one is just
893 splitLongestPrefix :: String -> (Char -> Bool) -> (String,String)
894 splitLongestPrefix s pred
896 [] -> ([], reverse suf)
897 (_:pre) -> (reverse pre, reverse suf)
898 where (suf,pre) = break pred (reverse s)
900 replaceFilenameSuffix :: FilePath -> Suffix -> FilePath
901 replaceFilenameSuffix s suf = removeSuffix '.' s ++ suf
903 -- directoryOf strips the filename off the input string, returning
905 directoryOf :: FilePath -> String
906 directoryOf = fst . splitFilenameDir
908 -- filenameOf strips the directory off the input string, returning
910 filenameOf :: FilePath -> String
911 filenameOf = snd . splitFilenameDir
913 replaceFilenameDirectory :: FilePath -> String -> FilePath
914 replaceFilenameDirectory s dir
915 = dir ++ '/':dropLongestPrefix s isPathSeparator
917 escapeSpaces :: String -> String
918 escapeSpaces = foldr (\c s -> if isSpace c then '\\':c:s else c:s) ""
920 isPathSeparator :: Char -> Bool
922 #ifdef mingw32_TARGET_OS
923 ch == '/' || ch == '\\'
928 -----------------------------------------------------------------------------
929 -- Convert filepath into platform / MSDOS form.
931 -- We maintain path names in Unix form ('/'-separated) right until
932 -- the last moment. On Windows we dos-ify them just before passing them
933 -- to the Windows command.
935 -- The alternative, of using '/' consistently on Unix and '\' on Windows,
936 -- proved quite awkward. There were a lot more calls to platformPath,
937 -- and even on Windows we might invoke a unix-like utility (eg 'sh'), which
938 -- interpreted a command line 'foo\baz' as 'foobaz'.
940 normalisePath :: String -> String
941 -- Just changes '\' to '/'
943 pgmPath :: String -- Directory string in Unix format
944 -> String -- Program name with no directory separators
946 -> String -- Program invocation string in native format
948 #if defined(mingw32_HOST_OS)
949 --------------------- Windows version ------------------
950 normalisePath xs = subst '\\' '/' xs
951 pgmPath dir pgm = platformPath dir ++ '\\' : pgm
952 platformPath p = subst '/' '\\' p
954 subst a b ls = map (\ x -> if x == a then b else x) ls
956 --------------------- Non-Windows version --------------
957 normalisePath xs = xs
958 pgmPath dir pgm = dir ++ '/' : pgm
959 platformPath stuff = stuff
960 --------------------------------------------------------