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,
72 #include "HsVersions.h"
74 import Panic ( panic, trace )
77 import EXCEPTION ( Exception(..), finally, throwDyn, catchDyn, throw )
78 import qualified EXCEPTION as Exception
79 import DYNAMIC ( Typeable )
80 import DATA_IOREF ( IORef, newIORef )
81 import UNSAFE_IO ( unsafePerformIO )
82 import DATA_IOREF ( readIORef, writeIORef )
84 import qualified List ( elem, notElem )
87 import List ( zipWith4 )
92 import Directory ( doesDirectoryExist, createDirectory )
93 import Char ( isUpper, isAlphaNum, isSpace, ord, isDigit )
99 %************************************************************************
101 \subsection{The Eager monad}
103 %************************************************************************
105 The @Eager@ monad is just an encoding of continuation-passing style,
106 used to allow you to express "do this and then that", mainly to avoid
107 space leaks. It's done with a type synonym to save bureaucracy.
112 type Eager ans a = (a -> ans) -> ans
114 runEager :: Eager a a -> a
115 runEager m = m (\x -> x)
117 appEager :: Eager ans a -> (a -> ans) -> ans
118 appEager m cont = m cont
120 thenEager :: Eager ans a -> (a -> Eager ans b) -> Eager ans b
121 thenEager m k cont = m (\r -> k r cont)
123 returnEager :: a -> Eager ans a
124 returnEager v cont = cont v
126 mapEager :: (a -> Eager ans b) -> [a] -> Eager ans [b]
127 mapEager f [] = returnEager []
128 mapEager f (x:xs) = f x `thenEager` \ y ->
129 mapEager f xs `thenEager` \ ys ->
134 %************************************************************************
136 \subsection{A for loop}
138 %************************************************************************
141 -- Compose a function with itself n times. (nth rather than twice)
142 nTimes :: Int -> (a -> a) -> (a -> a)
145 nTimes n f = f . nTimes (n-1) f
148 %************************************************************************
150 \subsection[Utils-lists]{General list processing}
152 %************************************************************************
155 filterOut :: (a->Bool) -> [a] -> [a]
156 -- Like filter, only reverses the sense of the test
158 filterOut p (x:xs) | p x = filterOut p xs
159 | otherwise = x : filterOut p xs
162 A paranoid @zip@ (and some @zipWith@ friends) that checks the lists
163 are of equal length. Alastair Reid thinks this should only happen if
164 DEBUGging on; hey, why not?
167 zipEqual :: String -> [a] -> [b] -> [(a,b)]
168 zipWithEqual :: String -> (a->b->c) -> [a]->[b]->[c]
169 zipWith3Equal :: String -> (a->b->c->d) -> [a]->[b]->[c]->[d]
170 zipWith4Equal :: String -> (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e]
174 zipWithEqual _ = zipWith
175 zipWith3Equal _ = zipWith3
176 zipWith4Equal _ = zipWith4
178 zipEqual msg [] [] = []
179 zipEqual msg (a:as) (b:bs) = (a,b) : zipEqual msg as bs
180 zipEqual msg as bs = panic ("zipEqual: unequal lists:"++msg)
182 zipWithEqual msg z (a:as) (b:bs)= z a b : zipWithEqual msg z as bs
183 zipWithEqual msg _ [] [] = []
184 zipWithEqual msg _ _ _ = panic ("zipWithEqual: unequal lists:"++msg)
186 zipWith3Equal msg z (a:as) (b:bs) (c:cs)
187 = z a b c : zipWith3Equal msg z as bs cs
188 zipWith3Equal msg _ [] [] [] = []
189 zipWith3Equal msg _ _ _ _ = panic ("zipWith3Equal: unequal lists:"++msg)
191 zipWith4Equal msg z (a:as) (b:bs) (c:cs) (d:ds)
192 = z a b c d : zipWith4Equal msg z as bs cs ds
193 zipWith4Equal msg _ [] [] [] [] = []
194 zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists:"++msg)
199 -- zipLazy is lazy in the second list (observe the ~)
201 zipLazy :: [a] -> [b] -> [(a,b)]
203 zipLazy (x:xs) ~(y:ys) = (x,y) : zipLazy xs ys
208 stretchZipWith :: (a -> Bool) -> b -> (a->b->c) -> [a] -> [b] -> [c]
209 -- (stretchZipWith p z f xs ys) stretches ys by inserting z in
210 -- the places where p returns *True*
212 stretchZipWith p z f [] ys = []
213 stretchZipWith p z f (x:xs) ys
214 | p x = f x z : stretchZipWith p z f xs ys
215 | otherwise = case ys of
217 (y:ys) -> f x y : stretchZipWith p z f xs ys
222 mapAndUnzip :: (a -> (b, c)) -> [a] -> ([b], [c])
224 mapAndUnzip f [] = ([],[])
228 (rs1, rs2) = mapAndUnzip f xs
232 mapAndUnzip3 :: (a -> (b, c, d)) -> [a] -> ([b], [c], [d])
234 mapAndUnzip3 f [] = ([],[],[])
235 mapAndUnzip3 f (x:xs)
238 (rs1, rs2, rs3) = mapAndUnzip3 f xs
240 (r1:rs1, r2:rs2, r3:rs3)
244 nOfThem :: Int -> a -> [a]
245 nOfThem n thing = replicate n thing
247 -- 'atLength atLen atEnd ls n' unravels list 'ls' to position 'n';
250 -- atLength atLenPred atEndPred ls n
251 -- | n < 0 = atLenPred n
252 -- | length ls < n = atEndPred (n - length ls)
253 -- | otherwise = atLenPred (drop n ls)
255 atLength :: ([a] -> b)
260 atLength atLenPred atEndPred ls n
261 | n < 0 = atEndPred n
262 | otherwise = go n ls
264 go n [] = atEndPred n
265 go 0 ls = atLenPred ls
266 go n (_:xs) = go (n-1) xs
269 lengthExceeds :: [a] -> Int -> Bool
270 -- (lengthExceeds xs n) = (length xs > n)
271 lengthExceeds = atLength notNull (const False)
273 lengthAtLeast :: [a] -> Int -> Bool
274 lengthAtLeast = atLength notNull (== 0)
276 lengthIs :: [a] -> Int -> Bool
277 lengthIs = atLength null (==0)
279 listLengthCmp :: [a] -> Int -> Ordering
280 listLengthCmp = atLength atLen atEnd
284 | x > 0 = LT -- not yet seen 'n' elts, so list length is < n.
290 isSingleton :: [a] -> Bool
291 isSingleton [x] = True
292 isSingleton _ = False
294 notNull :: [a] -> Bool
298 snocView :: [a] -> Maybe ([a],a)
299 -- Split off the last element
300 snocView [] = Nothing
301 snocView xs = go [] xs
303 -- Invariant: second arg is non-empty
304 go acc [x] = Just (reverse acc, x)
305 go acc (x:xs) = go (x:acc) xs
315 Debugging/specialising versions of \tr{elem} and \tr{notElem}
318 isIn, isn'tIn :: (Eq a) => String -> a -> [a] -> Bool
321 isIn msg x ys = elem__ x ys
322 isn'tIn msg x ys = notElem__ x ys
324 --these are here to be SPECIALIZEd (automagically)
326 elem__ x (y:ys) = x==y || elem__ x ys
328 notElem__ x [] = True
329 notElem__ x (y:ys) = x /= y && notElem__ x ys
333 = elem (_ILIT 0) x ys
337 | i ># _ILIT 100 = trace ("Over-long elem in " ++ msg) $
339 | otherwise = x == y || elem (i +# _ILIT(1)) x ys
342 = notElem (_ILIT 0) x ys
344 notElem i x [] = True
346 | i ># _ILIT 100 = trace ("Over-long notElem in " ++ msg) $
347 x `List.notElem` (y:ys)
348 | otherwise = x /= y && notElem (i +# _ILIT(1)) x ys
352 %************************************************************************
354 \subsubsection[Utils-Carsten-mergesort]{A mergesort from Carsten}
356 %************************************************************************
359 Date: Mon, 3 May 93 20:45:23 +0200
360 From: Carsten Kehler Holst <kehler@cs.chalmers.se>
361 To: partain@dcs.gla.ac.uk
362 Subject: natural merge sort beats quick sort [ and it is prettier ]
364 Here is a piece of Haskell code that I'm rather fond of. See it as an
365 attempt to get rid of the ridiculous quick-sort routine. group is
366 quite useful by itself I think it was John's idea originally though I
367 believe the lazy version is due to me [surprisingly complicated].
368 gamma [used to be called] is called gamma because I got inspired by
369 the Gamma calculus. It is not very close to the calculus but does
370 behave less sequentially than both foldr and foldl. One could imagine
371 a version of gamma that took a unit element as well thereby avoiding
372 the problem with empty lists.
374 I've tried this code against
376 1) insertion sort - as provided by haskell
377 2) the normal implementation of quick sort
378 3) a deforested version of quick sort due to Jan Sparud
379 4) a super-optimized-quick-sort of Lennart's
381 If the list is partially sorted both merge sort and in particular
382 natural merge sort wins. If the list is random [ average length of
383 rising subsequences = approx 2 ] mergesort still wins and natural
384 merge sort is marginally beaten by Lennart's soqs. The space
385 consumption of merge sort is a bit worse than Lennart's quick sort
386 approx a factor of 2. And a lot worse if Sparud's bug-fix [see his
387 fpca article ] isn't used because of group.
394 group :: (a -> a -> Bool) -> [a] -> [[a]]
395 -- Given a <= function, group finds maximal contiguous up-runs
396 -- or down-runs in the input list.
397 -- It's stable, in the sense that it never re-orders equal elements
399 -- Date: Mon, 12 Feb 1996 15:09:41 +0000
400 -- From: Andy Gill <andy@dcs.gla.ac.uk>
401 -- Here is a `better' definition of group.
404 group p (x:xs) = group' xs x x (x :)
406 group' [] _ _ s = [s []]
407 group' (x:xs) x_min x_max s
408 | x_max `p` x = group' xs x_min x (s . (x :))
409 | not (x_min `p` x) = group' xs x x_max ((x :) . s)
410 | otherwise = s [] : group' xs x x (x :)
411 -- NB: the 'not' is essential for stablity
412 -- x `p` x_min would reverse equal elements
414 generalMerge :: (a -> a -> Bool) -> [a] -> [a] -> [a]
415 generalMerge p xs [] = xs
416 generalMerge p [] ys = ys
417 generalMerge p (x:xs) (y:ys) | x `p` y = x : generalMerge p xs (y:ys)
418 | otherwise = y : generalMerge p (x:xs) ys
420 -- gamma is now called balancedFold
422 balancedFold :: (a -> a -> a) -> [a] -> a
423 balancedFold f [] = error "can't reduce an empty list using balancedFold"
424 balancedFold f [x] = x
425 balancedFold f l = balancedFold f (balancedFold' f l)
427 balancedFold' :: (a -> a -> a) -> [a] -> [a]
428 balancedFold' f (x:y:xs) = f x y : balancedFold' f xs
429 balancedFold' f xs = xs
431 generalNaturalMergeSort p [] = []
432 generalNaturalMergeSort p xs = (balancedFold (generalMerge p) . group p) xs
435 generalMergeSort p [] = []
436 generalMergeSort p xs = (balancedFold (generalMerge p) . map (: [])) xs
438 mergeSort, naturalMergeSort :: Ord a => [a] -> [a]
440 mergeSort = generalMergeSort (<=)
441 naturalMergeSort = generalNaturalMergeSort (<=)
443 mergeSortLe le = generalMergeSort le
446 sortLe :: (a->a->Bool) -> [a] -> [a]
447 sortLe le = generalNaturalMergeSort le
449 sortWith :: Ord b => (a->b) -> [a] -> [a]
450 sortWith get_key xs = sortLe le xs
452 x `le` y = get_key x < get_key y
455 %************************************************************************
457 \subsection[Utils-transitive-closure]{Transitive closure}
459 %************************************************************************
461 This algorithm for transitive closure is straightforward, albeit quadratic.
464 transitiveClosure :: (a -> [a]) -- Successor function
465 -> (a -> a -> Bool) -- Equality predicate
467 -> [a] -- The transitive closure
469 transitiveClosure succ eq xs
473 go done (x:xs) | x `is_in` done = go done xs
474 | otherwise = go (x:done) (succ x ++ xs)
477 x `is_in` (y:ys) | eq x y = True
478 | otherwise = x `is_in` ys
481 %************************************************************************
483 \subsection[Utils-accum]{Accumulating}
485 %************************************************************************
487 @mapAccumL@ behaves like a combination
488 of @map@ and @foldl@;
489 it applies a function to each element of a list, passing an accumulating
490 parameter from left to right, and returning a final value of this
491 accumulator together with the new list.
494 mapAccumL :: (acc -> x -> (acc, y)) -- Function of elt of input list
495 -- and accumulator, returning new
496 -- accumulator and elt of result list
497 -> acc -- Initial accumulator
499 -> (acc, [y]) -- Final accumulator and result list
501 mapAccumL f b [] = (b, [])
502 mapAccumL f b (x:xs) = (b'', x':xs') where
504 (b'', xs') = mapAccumL f b' xs
507 @mapAccumR@ does the same, but working from right to left instead. Its type is
508 the same as @mapAccumL@, though.
511 mapAccumR :: (acc -> x -> (acc, y)) -- Function of elt of input list
512 -- and accumulator, returning new
513 -- accumulator and elt of result list
514 -> acc -- Initial accumulator
516 -> (acc, [y]) -- Final accumulator and result list
518 mapAccumR f b [] = (b, [])
519 mapAccumR f b (x:xs) = (b'', x':xs') where
521 (b', xs') = mapAccumR f b xs
524 Here is the bi-directional version, that works from both left and right.
527 mapAccumB :: (accl -> accr -> x -> (accl, accr,y))
528 -- Function of elt of input list
529 -- and accumulator, returning new
530 -- accumulator and elt of result list
531 -> accl -- Initial accumulator from left
532 -> accr -- Initial accumulator from right
534 -> (accl, accr, [y]) -- Final accumulators and result list
536 mapAccumB f a b [] = (a,b,[])
537 mapAccumB f a b (x:xs) = (a'',b'',y:ys)
539 (a',b'',y) = f a b' x
540 (a'',b',ys) = mapAccumB f a' b xs
543 A strict version of foldl.
546 foldl' :: (a -> b -> a) -> a -> [b] -> a
547 foldl' f z xs = lgo z xs
550 lgo z (x:xs) = (lgo $! (f z x)) xs
553 A combination of foldl with zip. It works with equal length lists.
556 foldl2 :: (acc -> a -> b -> acc) -> acc -> [a] -> [b] -> acc
558 foldl2 k z (a:as) (b:bs) = foldl2 k (k z a b) as bs
561 Count the number of times a predicate is true
564 count :: (a -> Bool) -> [a] -> Int
566 count p (x:xs) | p x = 1 + count p xs
567 | otherwise = count p xs
570 @splitAt@, @take@, and @drop@ but with length of another
571 list giving the break-off point:
574 takeList :: [b] -> [a] -> [a]
579 (y:ys) -> y : takeList xs ys
581 dropList :: [b] -> [a] -> [a]
583 dropList _ xs@[] = xs
584 dropList (_:xs) (_:ys) = dropList xs ys
587 splitAtList :: [b] -> [a] -> ([a], [a])
588 splitAtList [] xs = ([], xs)
589 splitAtList _ xs@[] = (xs, xs)
590 splitAtList (_:xs) (y:ys) = (y:ys', ys'')
592 (ys', ys'') = splitAtList xs ys
594 split :: Char -> String -> [String]
595 split c s = case rest of
597 _:rest -> chunk : split c rest
598 where (chunk, rest) = break (==c) s
602 %************************************************************************
604 \subsection[Utils-comparison]{Comparisons}
606 %************************************************************************
609 isEqual :: Ordering -> Bool
610 -- Often used in (isEqual (a `compare` b))
615 thenCmp :: Ordering -> Ordering -> Ordering
616 {-# INLINE thenCmp #-}
618 thenCmp other any = other
620 eqListBy :: (a->a->Bool) -> [a] -> [a] -> Bool
621 eqListBy eq [] [] = True
622 eqListBy eq (x:xs) (y:ys) = eq x y && eqListBy eq xs ys
623 eqListBy eq xs ys = False
625 equalLength :: [a] -> [b] -> Bool
626 equalLength [] [] = True
627 equalLength (_:xs) (_:ys) = equalLength xs ys
628 equalLength xs ys = False
630 compareLength :: [a] -> [b] -> Ordering
631 compareLength [] [] = EQ
632 compareLength (_:xs) (_:ys) = compareLength xs ys
633 compareLength [] _ys = LT
634 compareLength _xs [] = GT
636 cmpList :: (a -> a -> Ordering) -> [a] -> [a] -> Ordering
637 -- `cmpList' uses a user-specified comparer
639 cmpList cmp [] [] = EQ
640 cmpList cmp [] _ = LT
641 cmpList cmp _ [] = GT
642 cmpList cmp (a:as) (b:bs)
643 = case cmp a b of { EQ -> cmpList cmp as bs; xxx -> xxx }
647 prefixMatch :: Eq a => [a] -> [a] -> Bool
648 prefixMatch [] _str = True
649 prefixMatch _pat [] = False
650 prefixMatch (p:ps) (s:ss) | p == s = prefixMatch ps ss
653 maybePrefixMatch :: String -> String -> Maybe String
654 maybePrefixMatch [] rest = Just rest
655 maybePrefixMatch (_:_) [] = Nothing
656 maybePrefixMatch (p:pat) (r:rest)
657 | p == r = maybePrefixMatch pat rest
658 | otherwise = Nothing
660 suffixMatch :: Eq a => [a] -> [a] -> Bool
661 suffixMatch pat str = prefixMatch (reverse pat) (reverse str)
663 removeSpaces :: String -> String
664 removeSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace
667 %************************************************************************
669 \subsection[Utils-pairs]{Pairs}
671 %************************************************************************
673 The following are curried versions of @fst@ and @snd@.
677 cfst :: a -> b -> a -- stranal-sem only (Note)
682 The following provide us higher order functions that, when applied
683 to a function, operate on pairs.
687 applyToPair :: ((a -> c),(b -> d)) -> (a,b) -> (c,d)
688 applyToPair (f,g) (x,y) = (f x, g y)
690 applyToFst :: (a -> c) -> (a,b)-> (c,b)
691 applyToFst f (x,y) = (f x,y)
693 applyToSnd :: (b -> d) -> (a,b) -> (a,d)
694 applyToSnd f (x,y) = (x,f y)
699 unzipWith :: (a -> b -> c) -> [(a, b)] -> [c]
700 unzipWith f pairs = map ( \ (a, b) -> f a b ) pairs
704 seqList :: [a] -> b -> b
706 seqList (x:xs) b = x `seq` seqList xs b
712 global :: a -> IORef a
713 global a = unsafePerformIO (newIORef a)
717 consIORef :: IORef [a] -> a -> IO ()
720 writeIORef var (x:xs)
726 looksLikeModuleName [] = False
727 looksLikeModuleName (c:cs) = isUpper c && go cs
729 go ('.':cs) = looksLikeModuleName cs
730 go (c:cs) = (isAlphaNum c || c == '_') && go cs
733 Akin to @Prelude.words@, but sensitive to dquoted entities treating
734 them as single words.
737 toArgs :: String -> [String]
740 case break (\ ch -> isSpace ch || ch == '"') (dropWhile isSpace s) of -- "
742 (\ ws -> if null w then ws else w : ws) $
746 | x /= '"' -> toArgs xs
749 ((str,rs):_) -> stripQuotes str : toArgs rs
752 -- strip away dquotes; assume first and last chars contain quotes.
753 stripQuotes :: String -> String
754 stripQuotes ('"':xs) = init xs
758 -- -----------------------------------------------------------------------------
762 readRational__ :: ReadS Rational -- NB: doesn't handle leading "-"
763 readRational__ r = do
766 return ((n%1)*10^^(k-d), t)
769 (ds,s) <- lexDecDigits r
770 (ds',t) <- lexDotDigits s
771 return (read (ds++ds'), length ds', t)
773 readExp (e:s) | e `elem` "eE" = readExp' s
774 readExp s = return (0,s)
776 readExp' ('+':s) = readDec s
777 readExp' ('-':s) = do
780 readExp' s = readDec s
783 (ds,r) <- nonnull isDigit s
784 return (foldl1 (\n d -> n * 10 + d) [ ord d - ord '0' | d <- ds ],
787 lexDecDigits = nonnull isDigit
789 lexDotDigits ('.':s) = return (span isDigit s)
790 lexDotDigits s = return ("",s)
792 nonnull p s = do (cs@(_:_),t) <- return (span p s)
795 readRational :: String -> Rational -- NB: *does* handle a leading "-"
798 '-' : xs -> - (read_me xs)
802 = case (do { (x,"") <- readRational__ s ; return x }) of
804 [] -> error ("readRational: no parse:" ++ top_s)
805 _ -> error ("readRational: ambiguous parse:" ++ top_s)
808 -----------------------------------------------------------------------------
809 -- Create a hierarchy of directories
811 createDirectoryHierarchy :: FilePath -> IO ()
812 createDirectoryHierarchy dir = do
813 b <- doesDirectoryExist dir
815 createDirectoryHierarchy (directoryOf dir)
818 -----------------------------------------------------------------------------
819 -- Verify that the 'dirname' portion of a FilePath exists.
821 doesDirNameExist :: FilePath -> IO Bool
822 doesDirNameExist fpath = doesDirectoryExist (directoryOf fpath)
824 -- -----------------------------------------------------------------------------
829 handleDyn :: Typeable ex => (ex -> IO a) -> IO a -> IO a
830 handleDyn = flip catchDyn
832 handle :: (Exception -> IO a) -> IO a -> IO a
833 #if __GLASGOW_HASKELL__ < 501
834 handle = flip Exception.catchAllIO
836 handle h f = f `Exception.catch` \e -> case e of
837 ExitException _ -> throw e
841 -- --------------------------------------------------------------
842 -- Filename manipulation
846 splitFilename :: String -> (String,Suffix)
847 splitFilename f = splitLongestPrefix f (=='.')
849 getFileSuffix :: String -> Suffix
850 getFileSuffix f = dropLongestPrefix f (=='.')
852 -- "foo/bar/xyzzy.ext" -> ("foo/bar", "xyzzy.ext")
853 splitFilenameDir :: String -> (String,String)
855 = let (dir, rest) = splitLongestPrefix str isPathSeparator
856 real_dir | null dir = "."
860 -- "foo/bar/xyzzy.ext" -> ("foo/bar", "xyzzy", ".ext")
861 splitFilename3 :: String -> (String,String,Suffix)
863 = let (dir, rest) = splitLongestPrefix str isPathSeparator
864 (name, ext) = splitFilename rest
865 real_dir | null dir = "."
867 in (real_dir, name, ext)
869 removeSuffix :: Char -> String -> Suffix
872 | otherwise = reverse pre
873 where (suf,pre) = break (==c) (reverse s)
875 dropLongestPrefix :: String -> (Char -> Bool) -> String
876 dropLongestPrefix s pred = reverse suf
877 where (suf,_pre) = break pred (reverse s)
879 takeLongestPrefix :: String -> (Char -> Bool) -> String
880 takeLongestPrefix s pred = reverse pre
881 where (_suf,pre) = break pred (reverse s)
883 -- split a string at the last character where 'pred' is True,
884 -- returning a pair of strings. The first component holds the string
885 -- up (but not including) the last character for which 'pred' returned
886 -- True, the second whatever comes after (but also not including the
889 -- If 'pred' returns False for all characters in the string, the original
890 -- string is returned in the second component (and the first one is just
892 splitLongestPrefix :: String -> (Char -> Bool) -> (String,String)
893 splitLongestPrefix s pred
895 [] -> ([], reverse suf)
896 (_:pre) -> (reverse pre, reverse suf)
897 where (suf,pre) = break pred (reverse s)
899 replaceFilenameSuffix :: FilePath -> Suffix -> FilePath
900 replaceFilenameSuffix s suf = removeSuffix '.' s ++ suf
902 -- directoryOf strips the filename off the input string, returning
904 directoryOf :: FilePath -> String
905 directoryOf = fst . splitFilenameDir
907 -- filenameOf strips the directory off the input string, returning
909 filenameOf :: FilePath -> String
910 filenameOf = snd . splitFilenameDir
912 replaceFilenameDirectory :: FilePath -> String -> FilePath
913 replaceFilenameDirectory s dir
914 = dir ++ '/':dropLongestPrefix s isPathSeparator
916 escapeSpaces :: String -> String
917 escapeSpaces = foldr (\c s -> if isSpace c then '\\':c:s else c:s) ""
919 isPathSeparator :: Char -> Bool
921 #ifdef mingw32_TARGET_OS
922 ch == '/' || ch == '\\'