2 % (c) The University of Glasgow 2006
3 % (c) The University of Glasgow 1992-2002
5 \section[Util]{Highly random utility functions}
9 debugIsOn, isWindowsHost, isWindowsTarget, isDarwinTarget,
11 -- general list processing
12 zipEqual, zipWithEqual, zipWith3Equal, zipWith4Equal,
13 zipLazy, stretchZipWith,
15 mapAndUnzip, mapAndUnzip3,
16 nOfThem, filterOut, partitionWith, splitEithers,
19 lengthExceeds, lengthIs, lengthAtLeast,
20 listLengthCmp, atLength, equalLength, compareLength,
22 isSingleton, only, singleton,
33 -- transitive closures
39 takeList, dropList, splitAtList, split,
43 thenCmp, cmpList, maybePrefixMatch,
57 getCmd, toCmdArgs, toArgs,
59 -- Floating point stuff
63 createDirectoryHierarchy,
65 modificationTimeIfExists,
67 later, handleDyn, handle,
74 Direction(..), reslash,
77 #include "HsVersions.h"
81 import Control.Exception ( Exception(..), finally, catchDyn, throw )
82 import qualified Control.Exception as Exception
83 import Data.Dynamic ( Typeable )
84 import Data.IORef ( IORef, newIORef )
85 import System.IO.Unsafe ( unsafePerformIO )
86 import Data.IORef ( readIORef, writeIORef )
87 import Data.List hiding (group)
89 import qualified Data.List as List ( elem )
91 import qualified Data.List as List ( notElem )
95 import Control.Monad ( unless )
96 import SYSTEM_IO_ERROR as IO ( catch, isDoesNotExistError )
97 import System.Directory ( doesDirectoryExist, createDirectory,
99 import System.FilePath hiding ( searchPathSeparator )
100 import Data.Char ( isUpper, isAlphaNum, isSpace, ord, isDigit )
101 import Data.Ratio ( (%) )
102 import System.Time ( ClockTime )
107 %************************************************************************
109 \subsection{Is DEBUG on, are we on Windows?}
111 %************************************************************************
121 isWindowsHost :: Bool
122 #ifdef mingw32_HOST_OS
125 isWindowsHost = False
128 isWindowsTarget :: Bool
129 #ifdef mingw32_TARGET_OS
130 isWindowsTarget = True
132 isWindowsTarget = False
135 isDarwinTarget :: Bool
136 #ifdef darwin_TARGET_OS
137 isDarwinTarget = True
139 isDarwinTarget = False
143 %************************************************************************
145 \subsection{A for loop}
147 %************************************************************************
150 -- Compose a function with itself n times. (nth rather than twice)
151 nTimes :: Int -> (a -> a) -> (a -> a)
154 nTimes n f = f . nTimes (n-1) f
157 %************************************************************************
159 \subsection[Utils-lists]{General list processing}
161 %************************************************************************
164 filterOut :: (a->Bool) -> [a] -> [a]
165 -- Like filter, only reverses the sense of the test
167 filterOut p (x:xs) | p x = filterOut p xs
168 | otherwise = x : filterOut p xs
170 partitionWith :: (a -> Either b c) -> [a] -> ([b], [c])
171 partitionWith _ [] = ([],[])
172 partitionWith f (x:xs) = case f x of
174 Right c -> (bs, c:cs)
175 where (bs,cs) = partitionWith f xs
177 splitEithers :: [Either a b] -> ([a], [b])
178 splitEithers [] = ([],[])
179 splitEithers (e : es) = case e of
181 Right y -> (xs, y:ys)
182 where (xs,ys) = splitEithers es
185 A paranoid @zip@ (and some @zipWith@ friends) that checks the lists
186 are of equal length. Alastair Reid thinks this should only happen if
187 DEBUGging on; hey, why not?
190 zipEqual :: String -> [a] -> [b] -> [(a,b)]
191 zipWithEqual :: String -> (a->b->c) -> [a]->[b]->[c]
192 zipWith3Equal :: String -> (a->b->c->d) -> [a]->[b]->[c]->[d]
193 zipWith4Equal :: String -> (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e]
197 zipWithEqual _ = zipWith
198 zipWith3Equal _ = zipWith3
199 zipWith4Equal _ = zipWith4
201 zipEqual _ [] [] = []
202 zipEqual msg (a:as) (b:bs) = (a,b) : zipEqual msg as bs
203 zipEqual msg _ _ = panic ("zipEqual: unequal lists:"++msg)
205 zipWithEqual msg z (a:as) (b:bs)= z a b : zipWithEqual msg z as bs
206 zipWithEqual _ _ [] [] = []
207 zipWithEqual msg _ _ _ = panic ("zipWithEqual: unequal lists:"++msg)
209 zipWith3Equal msg z (a:as) (b:bs) (c:cs)
210 = z a b c : zipWith3Equal msg z as bs cs
211 zipWith3Equal _ _ [] [] [] = []
212 zipWith3Equal msg _ _ _ _ = panic ("zipWith3Equal: unequal lists:"++msg)
214 zipWith4Equal msg z (a:as) (b:bs) (c:cs) (d:ds)
215 = z a b c d : zipWith4Equal msg z as bs cs ds
216 zipWith4Equal _ _ [] [] [] [] = []
217 zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists:"++msg)
222 -- zipLazy is lazy in the second list (observe the ~)
224 zipLazy :: [a] -> [b] -> [(a,b)]
226 -- We want to write this, but with GHC 6.4 we get a warning, so it
228 -- zipLazy (x:xs) ~(y:ys) = (x,y) : zipLazy xs ys
229 -- so we write this instead:
230 zipLazy (x:xs) zs = let y : ys = zs
231 in (x,y) : zipLazy xs ys
236 stretchZipWith :: (a -> Bool) -> b -> (a->b->c) -> [a] -> [b] -> [c]
237 -- (stretchZipWith p z f xs ys) stretches ys by inserting z in
238 -- the places where p returns *True*
240 stretchZipWith _ _ _ [] _ = []
241 stretchZipWith p z f (x:xs) ys
242 | p x = f x z : stretchZipWith p z f xs ys
243 | otherwise = case ys of
245 (y:ys) -> f x y : stretchZipWith p z f xs ys
250 mapFst :: (a->c) -> [(a,b)] -> [(c,b)]
251 mapSnd :: (b->c) -> [(a,b)] -> [(a,c)]
253 mapFst f xys = [(f x, y) | (x,y) <- xys]
254 mapSnd f xys = [(x, f y) | (x,y) <- xys]
256 mapAndUnzip :: (a -> (b, c)) -> [a] -> ([b], [c])
258 mapAndUnzip _ [] = ([], [])
261 (rs1, rs2) = mapAndUnzip f xs
265 mapAndUnzip3 :: (a -> (b, c, d)) -> [a] -> ([b], [c], [d])
267 mapAndUnzip3 _ [] = ([], [], [])
268 mapAndUnzip3 f (x:xs)
269 = let (r1, r2, r3) = f x
270 (rs1, rs2, rs3) = mapAndUnzip3 f xs
272 (r1:rs1, r2:rs2, r3:rs3)
276 nOfThem :: Int -> a -> [a]
277 nOfThem n thing = replicate n thing
279 -- 'atLength atLen atEnd ls n' unravels list 'ls' to position 'n';
282 -- atLength atLenPred atEndPred ls n
283 -- | n < 0 = atLenPred n
284 -- | length ls < n = atEndPred (n - length ls)
285 -- | otherwise = atLenPred (drop n ls)
287 atLength :: ([a] -> b)
292 atLength atLenPred atEndPred ls n
293 | n < 0 = atEndPred n
294 | otherwise = go n ls
296 go n [] = atEndPred n
297 go 0 ls = atLenPred ls
298 go n (_:xs) = go (n-1) xs
301 lengthExceeds :: [a] -> Int -> Bool
302 -- (lengthExceeds xs n) = (length xs > n)
303 lengthExceeds = atLength notNull (const False)
305 lengthAtLeast :: [a] -> Int -> Bool
306 lengthAtLeast = atLength notNull (== 0)
308 lengthIs :: [a] -> Int -> Bool
309 lengthIs = atLength null (==0)
311 listLengthCmp :: [a] -> Int -> Ordering
312 listLengthCmp = atLength atLen atEnd
316 | x > 0 = LT -- not yet seen 'n' elts, so list length is < n.
322 equalLength :: [a] -> [b] -> Bool
323 equalLength [] [] = True
324 equalLength (_:xs) (_:ys) = equalLength xs ys
325 equalLength _ _ = False
327 compareLength :: [a] -> [b] -> Ordering
328 compareLength [] [] = EQ
329 compareLength (_:xs) (_:ys) = compareLength xs ys
330 compareLength [] _ = LT
331 compareLength _ [] = GT
333 ----------------------------
334 singleton :: a -> [a]
337 isSingleton :: [a] -> Bool
338 isSingleton [_] = True
339 isSingleton _ = False
341 notNull :: [a] -> Bool
351 only _ = panic "Util: only"
354 Debugging/specialising versions of \tr{elem} and \tr{notElem}
357 isIn, isn'tIn :: Eq a => String -> a -> [a] -> Bool
360 isIn _msg x ys = elem__ x ys
361 isn'tIn _msg x ys = notElem__ x ys
363 --these are here to be SPECIALIZEd (automagically)
364 elem__ :: Eq a => a -> [a] -> Bool
366 elem__ x (y:ys) = x == y || elem__ x ys
368 notElem__ :: Eq a => a -> [a] -> Bool
369 notElem__ _ [] = True
370 notElem__ x (y:ys) = x /= y && notElem__ x ys
374 = elem (_ILIT(0)) x ys
378 | i ># _ILIT(100) = trace ("Over-long elem in " ++ msg)
379 (x `List.elem` (y:ys))
380 | otherwise = x == y || elem (i +# _ILIT(1)) x ys
383 = notElem (_ILIT(0)) x ys
385 notElem _ _ [] = True
387 | i ># _ILIT(100) = trace ("Over-long notElem in " ++ msg)
388 (x `List.notElem` (y:ys))
389 | otherwise = x /= y && notElem (i +# _ILIT(1)) x ys
393 foldl1' was added in GHC 6.4
396 #if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 604
397 foldl1' :: (a -> a -> a) -> [a] -> a
398 foldl1' f (x:xs) = foldl' f x xs
399 foldl1' _ [] = panic "foldl1'"
403 %************************************************************************
405 \subsubsection[Utils-Carsten-mergesort]{A mergesort from Carsten}
407 %************************************************************************
410 Date: Mon, 3 May 93 20:45:23 +0200
411 From: Carsten Kehler Holst <kehler@cs.chalmers.se>
412 To: partain@dcs.gla.ac.uk
413 Subject: natural merge sort beats quick sort [ and it is prettier ]
415 Here is a piece of Haskell code that I'm rather fond of. See it as an
416 attempt to get rid of the ridiculous quick-sort routine. group is
417 quite useful by itself I think it was John's idea originally though I
418 believe the lazy version is due to me [surprisingly complicated].
419 gamma [used to be called] is called gamma because I got inspired by
420 the Gamma calculus. It is not very close to the calculus but does
421 behave less sequentially than both foldr and foldl. One could imagine
422 a version of gamma that took a unit element as well thereby avoiding
423 the problem with empty lists.
425 I've tried this code against
427 1) insertion sort - as provided by haskell
428 2) the normal implementation of quick sort
429 3) a deforested version of quick sort due to Jan Sparud
430 4) a super-optimized-quick-sort of Lennart's
432 If the list is partially sorted both merge sort and in particular
433 natural merge sort wins. If the list is random [ average length of
434 rising subsequences = approx 2 ] mergesort still wins and natural
435 merge sort is marginally beaten by Lennart's soqs. The space
436 consumption of merge sort is a bit worse than Lennart's quick sort
437 approx a factor of 2. And a lot worse if Sparud's bug-fix [see his
438 fpca article ] isn't used because of group.
445 group :: (a -> a -> Bool) -> [a] -> [[a]]
446 -- Given a <= function, group finds maximal contiguous up-runs
447 -- or down-runs in the input list.
448 -- It's stable, in the sense that it never re-orders equal elements
450 -- Date: Mon, 12 Feb 1996 15:09:41 +0000
451 -- From: Andy Gill <andy@dcs.gla.ac.uk>
452 -- Here is a `better' definition of group.
455 group p (x:xs) = group' xs x x (x :)
457 group' [] _ _ s = [s []]
458 group' (x:xs) x_min x_max s
459 | x_max `p` x = group' xs x_min x (s . (x :))
460 | not (x_min `p` x) = group' xs x x_max ((x :) . s)
461 | otherwise = s [] : group' xs x x (x :)
462 -- NB: the 'not' is essential for stablity
463 -- x `p` x_min would reverse equal elements
465 generalMerge :: (a -> a -> Bool) -> [a] -> [a] -> [a]
466 generalMerge _ xs [] = xs
467 generalMerge _ [] ys = ys
468 generalMerge p (x:xs) (y:ys) | x `p` y = x : generalMerge p xs (y:ys)
469 | otherwise = y : generalMerge p (x:xs) ys
471 -- gamma is now called balancedFold
473 balancedFold :: (a -> a -> a) -> [a] -> a
474 balancedFold _ [] = error "can't reduce an empty list using balancedFold"
475 balancedFold _ [x] = x
476 balancedFold f l = balancedFold f (balancedFold' f l)
478 balancedFold' :: (a -> a -> a) -> [a] -> [a]
479 balancedFold' f (x:y:xs) = f x y : balancedFold' f xs
480 balancedFold' _ xs = xs
482 generalNaturalMergeSort :: (a -> a -> Bool) -> [a] -> [a]
483 generalNaturalMergeSort _ [] = []
484 generalNaturalMergeSort p xs = (balancedFold (generalMerge p) . group p) xs
487 generalMergeSort p [] = []
488 generalMergeSort p xs = (balancedFold (generalMerge p) . map (: [])) xs
490 mergeSort, naturalMergeSort :: Ord a => [a] -> [a]
492 mergeSort = generalMergeSort (<=)
493 naturalMergeSort = generalNaturalMergeSort (<=)
495 mergeSortLe le = generalMergeSort le
498 sortLe :: (a->a->Bool) -> [a] -> [a]
499 sortLe le = generalNaturalMergeSort le
501 sortWith :: Ord b => (a->b) -> [a] -> [a]
502 sortWith get_key xs = sortLe le xs
504 x `le` y = get_key x < get_key y
506 on :: (a -> a -> Ordering) -> (b -> a) -> b -> b -> Ordering
507 on cmp sel = \x y -> sel x `cmp` sel y
511 %************************************************************************
513 \subsection[Utils-transitive-closure]{Transitive closure}
515 %************************************************************************
517 This algorithm for transitive closure is straightforward, albeit quadratic.
520 transitiveClosure :: (a -> [a]) -- Successor function
521 -> (a -> a -> Bool) -- Equality predicate
523 -> [a] -- The transitive closure
525 transitiveClosure succ eq xs
529 go done (x:xs) | x `is_in` done = go done xs
530 | otherwise = go (x:done) (succ x ++ xs)
533 x `is_in` (y:ys) | eq x y = True
534 | otherwise = x `is_in` ys
537 %************************************************************************
539 \subsection[Utils-accum]{Accumulating}
541 %************************************************************************
543 A combination of foldl with zip. It works with equal length lists.
546 foldl2 :: (acc -> a -> b -> acc) -> acc -> [a] -> [b] -> acc
548 foldl2 k z (a:as) (b:bs) = foldl2 k (k z a b) as bs
549 foldl2 _ _ _ _ = panic "Util: foldl2"
551 all2 :: (a -> b -> Bool) -> [a] -> [b] -> Bool
552 -- True if the lists are the same length, and
553 -- all corresponding elements satisfy the predicate
555 all2 p (x:xs) (y:ys) = p x y && all2 p xs ys
559 Count the number of times a predicate is true
562 count :: (a -> Bool) -> [a] -> Int
564 count p (x:xs) | p x = 1 + count p xs
565 | otherwise = count p xs
568 @splitAt@, @take@, and @drop@ but with length of another
569 list giving the break-off point:
572 takeList :: [b] -> [a] -> [a]
577 (y:ys) -> y : takeList xs ys
579 dropList :: [b] -> [a] -> [a]
581 dropList _ xs@[] = xs
582 dropList (_:xs) (_:ys) = dropList xs ys
585 splitAtList :: [b] -> [a] -> ([a], [a])
586 splitAtList [] xs = ([], xs)
587 splitAtList _ xs@[] = (xs, xs)
588 splitAtList (_:xs) (y:ys) = (y:ys', ys'')
590 (ys', ys'') = splitAtList xs ys
592 snocView :: [a] -> Maybe ([a],a)
593 -- Split off the last element
594 snocView [] = Nothing
595 snocView xs = go [] xs
597 -- Invariant: second arg is non-empty
598 go acc [x] = Just (reverse acc, x)
599 go acc (x:xs) = go (x:acc) xs
600 go _ [] = panic "Util: snocView"
602 split :: Char -> String -> [String]
603 split c s = case rest of
605 _:rest -> chunk : split c rest
606 where (chunk, rest) = break (==c) s
610 %************************************************************************
612 \subsection[Utils-comparison]{Comparisons}
614 %************************************************************************
617 isEqual :: Ordering -> Bool
618 -- Often used in (isEqual (a `compare` b))
623 thenCmp :: Ordering -> Ordering -> Ordering
624 {-# INLINE thenCmp #-}
625 thenCmp EQ ordering = ordering
626 thenCmp ordering _ = ordering
628 eqListBy :: (a->a->Bool) -> [a] -> [a] -> Bool
629 eqListBy _ [] [] = True
630 eqListBy eq (x:xs) (y:ys) = eq x y && eqListBy eq xs ys
631 eqListBy _ _ _ = False
633 cmpList :: (a -> a -> Ordering) -> [a] -> [a] -> Ordering
634 -- `cmpList' uses a user-specified comparer
639 cmpList cmp (a:as) (b:bs)
640 = case cmp a b of { EQ -> cmpList cmp as bs; xxx -> xxx }
644 -- This (with a more general type) is Data.List.stripPrefix from GHC 6.8.
645 -- This definition can be removed once we require at least 6.8 to build.
646 maybePrefixMatch :: String -> String -> Maybe String
647 maybePrefixMatch [] rest = Just rest
648 maybePrefixMatch (_:_) [] = Nothing
649 maybePrefixMatch (p:pat) (r:rest)
650 | p == r = maybePrefixMatch pat rest
651 | otherwise = Nothing
653 removeSpaces :: String -> String
654 removeSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace
657 %************************************************************************
659 \subsection[Utils-pairs]{Pairs}
661 %************************************************************************
664 unzipWith :: (a -> b -> c) -> [(a, b)] -> [c]
665 unzipWith f pairs = map ( \ (a, b) -> f a b ) pairs
669 seqList :: [a] -> b -> b
671 seqList (x:xs) b = x `seq` seqList xs b
677 global :: a -> IORef a
678 global a = unsafePerformIO (newIORef a)
682 consIORef :: IORef [a] -> a -> IO ()
685 writeIORef var (x:xs)
691 looksLikeModuleName :: String -> Bool
692 looksLikeModuleName [] = False
693 looksLikeModuleName (c:cs) = isUpper c && go cs
695 go ('.':cs) = looksLikeModuleName cs
696 go (c:cs) = (isAlphaNum c || c == '_') && go cs
699 Akin to @Prelude.words@, but acts like the Bourne shell, treating
700 quoted strings as Haskell Strings, and also parses Haskell [String]
704 getCmd :: String -> Either String -- Error
705 (String, String) -- (Cmd, Rest)
706 getCmd s = case break isSpace $ dropWhile isSpace s of
707 ([], _) -> Left ("Couldn't find command in " ++ show s)
710 toCmdArgs :: String -> Either String -- Error
711 (String, [String]) -- (Cmd, Args)
712 toCmdArgs s = case getCmd s of
714 Right (cmd, s') -> case toArgs s' of
716 Right args -> Right (cmd, args)
718 toArgs :: String -> Either String -- Error
721 = case dropWhile isSpace str of
722 s@('[':_) -> case reads s of
724 | all isSpace spaces ->
727 Left ("Couldn't read " ++ show str ++ "as [String]")
730 toArgs' s = case dropWhile isSpace s of
732 ('"' : _) -> case reads s of
734 -- rest must either be [] or start with a space
735 | all isSpace (take 1 rest) ->
738 Right args -> Right (arg : args)
740 Left ("Couldn't read " ++ show s ++ "as String")
741 s' -> case break isSpace s' of
742 (arg, s'') -> case toArgs' s'' of
744 Right args -> Right (arg : args)
747 -- -----------------------------------------------------------------------------
751 readRational__ :: ReadS Rational -- NB: doesn't handle leading "-"
752 readRational__ r = do
755 return ((n%1)*10^^(k-d), t)
758 (ds,s) <- lexDecDigits r
759 (ds',t) <- lexDotDigits s
760 return (read (ds++ds'), length ds', t)
762 readExp (e:s) | e `elem` "eE" = readExp' s
763 readExp s = return (0,s)
765 readExp' ('+':s) = readDec s
766 readExp' ('-':s) = do (k,t) <- readDec s
768 readExp' s = readDec s
771 (ds,r) <- nonnull isDigit s
772 return (foldl1 (\n d -> n * 10 + d) [ ord d - ord '0' | d <- ds ],
775 lexDecDigits = nonnull isDigit
777 lexDotDigits ('.':s) = return (span isDigit s)
778 lexDotDigits s = return ("",s)
780 nonnull p s = do (cs@(_:_),t) <- return (span p s)
783 readRational :: String -> Rational -- NB: *does* handle a leading "-"
786 '-' : xs -> - (read_me xs)
790 = case (do { (x,"") <- readRational__ s ; return x }) of
792 [] -> error ("readRational: no parse:" ++ top_s)
793 _ -> error ("readRational: ambiguous parse:" ++ top_s)
796 -----------------------------------------------------------------------------
797 -- Create a hierarchy of directories
799 createDirectoryHierarchy :: FilePath -> IO ()
800 createDirectoryHierarchy dir | isDrive dir = return () -- XXX Hack
801 createDirectoryHierarchy dir = do
802 b <- doesDirectoryExist dir
803 unless b $ do createDirectoryHierarchy (takeDirectory dir)
806 -----------------------------------------------------------------------------
807 -- Verify that the 'dirname' portion of a FilePath exists.
809 doesDirNameExist :: FilePath -> IO Bool
810 doesDirNameExist fpath = case takeDirectory fpath of
811 "" -> return True -- XXX Hack
812 _ -> doesDirectoryExist (takeDirectory fpath)
814 -- -----------------------------------------------------------------------------
817 later :: IO b -> IO a -> IO a
820 handleDyn :: Typeable ex => (ex -> IO a) -> IO a -> IO a
821 handleDyn = flip catchDyn
823 handle :: (Exception -> IO a) -> IO a -> IO a
824 handle h f = f `Exception.catch` \e -> case e of
825 ExitException _ -> throw e
828 -- --------------------------------------------------------------
829 -- check existence & modification time at the same time
831 modificationTimeIfExists :: FilePath -> IO (Maybe ClockTime)
832 modificationTimeIfExists f = do
833 (do t <- getModificationTime f; return (Just t))
834 `IO.catch` \e -> if isDoesNotExistError e
838 -- split a string at the last character where 'pred' is True,
839 -- returning a pair of strings. The first component holds the string
840 -- up (but not including) the last character for which 'pred' returned
841 -- True, the second whatever comes after (but also not including the
844 -- If 'pred' returns False for all characters in the string, the original
845 -- string is returned in the first component (and the second one is just
847 splitLongestPrefix :: String -> (Char -> Bool) -> (String,String)
848 splitLongestPrefix str pred
849 | null r_pre = (str, [])
850 | otherwise = (reverse (tail r_pre), reverse r_suf)
851 -- 'tail' drops the char satisfying 'pred'
852 where (r_suf, r_pre) = break pred (reverse str)
854 escapeSpaces :: String -> String
855 escapeSpaces = foldr (\c s -> if isSpace c then '\\':c:s else c:s) ""
859 --------------------------------------------------------------
861 --------------------------------------------------------------
863 -- | The function splits the given string to substrings
864 -- using the 'searchPathSeparator'.
865 parseSearchPath :: String -> [FilePath]
866 parseSearchPath path = split path
868 split :: String -> [String]
872 _:rest -> chunk : split rest
876 #ifdef mingw32_HOST_OS
877 ('\"':xs@(_:_)) | last xs == '\"' -> init xs
881 (chunk', rest') = break (==searchPathSeparator) s
883 -- | A platform-specific character used to separate search path strings in
884 -- environment variables. The separator is a colon (\":\") on Unix and
885 -- Macintosh, and a semicolon (\";\") on the Windows operating system.
886 searchPathSeparator :: Char
887 #if mingw32_HOST_OS || mingw32_TARGET_OS
888 searchPathSeparator = ';'
890 searchPathSeparator = ':'
893 data Direction = Forwards | Backwards
895 reslash :: Direction -> FilePath -> FilePath
897 where f ('/' : xs) = slash : f xs
898 f ('\\' : xs) = slash : f xs
899 f (x : xs) = x : f xs