2 % (c) The University of Glasgow 2006
3 % (c) The University of Glasgow 1992-2002
7 -- | Highly random utility functions
9 -- * Flags dependent on the compiler build
10 ghciSupported, debugIsOn, ghciTablesNextToCode, picIsOn,
11 isWindowsHost, isWindowsTarget, isDarwinTarget,
13 -- * General list processing
14 zipEqual, zipWithEqual, zipWith3Equal, zipWith4Equal,
15 zipLazy, stretchZipWith,
20 mapAndUnzip, mapAndUnzip3,
21 nOfThem, filterOut, partitionWith, splitEithers,
23 foldl1', foldl2, count, all2,
25 lengthExceeds, lengthIs, lengthAtLeast,
26 listLengthCmp, atLength, equalLength, compareLength,
28 isSingleton, only, singleton,
33 -- * List operations controlled by another list
34 takeList, dropList, splitAtList, split,
45 thenCmp, cmpList, maybePrefixMatch,
48 -- * Transitive closures
57 -- * Argument processing
58 getCmd, toCmdArgs, toArgs,
64 createDirectoryHierarchy,
66 modificationTimeIfExists,
70 -- * Filenames and paths
75 Direction(..), reslash,
78 #include "HsVersions.h"
82 import Data.IORef ( IORef, newIORef )
83 import System.IO.Unsafe ( unsafePerformIO )
84 import Data.IORef ( readIORef, writeIORef )
85 import Data.List hiding (group)
87 import qualified Data.List as List ( elem )
89 import qualified Data.List as List ( notElem )
93 import Control.Monad ( unless )
94 import System.IO.Error as IO ( catch, isDoesNotExistError )
95 import System.Directory ( doesDirectoryExist, createDirectory,
97 import System.FilePath
98 import Data.Char ( isUpper, isAlphaNum, isSpace, ord, isDigit )
99 import Data.Ratio ( (%) )
100 import System.Time ( ClockTime )
105 %************************************************************************
107 \subsection{Is DEBUG on, are we on Windows, etc?}
109 %************************************************************************
111 These booleans are global constants, set by CPP flags. They allow us to
112 recompile a single module (this one) to change whether or not debug output
113 appears. They sometimes let us avoid even running CPP elsewhere.
115 It's important that the flags are literal constants (True/False). Then,
116 with -0, tests of the flags in other modules will simplify to the correct
117 branch of the conditional, thereby dropping debug code altogether when
121 ghciSupported :: Bool
125 ghciSupported = False
135 ghciTablesNextToCode :: Bool
136 #ifdef GHCI_TABLES_NEXT_TO_CODE
137 ghciTablesNextToCode = True
139 ghciTablesNextToCode = False
149 isWindowsHost :: Bool
150 #ifdef mingw32_HOST_OS
153 isWindowsHost = False
156 isWindowsTarget :: Bool
157 #ifdef mingw32_TARGET_OS
158 isWindowsTarget = True
160 isWindowsTarget = False
163 isDarwinTarget :: Bool
164 #ifdef darwin_TARGET_OS
165 isDarwinTarget = True
167 isDarwinTarget = False
171 %************************************************************************
173 \subsection{A for loop}
175 %************************************************************************
178 -- | Compose a function with itself n times. (nth rather than twice)
179 nTimes :: Int -> (a -> a) -> (a -> a)
182 nTimes n f = f . nTimes (n-1) f
185 %************************************************************************
187 \subsection[Utils-lists]{General list processing}
189 %************************************************************************
192 filterOut :: (a->Bool) -> [a] -> [a]
193 -- ^ Like filter, only it reverses the sense of the test
195 filterOut p (x:xs) | p x = filterOut p xs
196 | otherwise = x : filterOut p xs
198 partitionWith :: (a -> Either b c) -> [a] -> ([b], [c])
199 -- ^ Uses a function to determine which of two output lists an input element should join
200 partitionWith _ [] = ([],[])
201 partitionWith f (x:xs) = case f x of
203 Right c -> (bs, c:cs)
204 where (bs,cs) = partitionWith f xs
206 splitEithers :: [Either a b] -> ([a], [b])
207 -- ^ Teases a list of 'Either's apart into two lists
208 splitEithers [] = ([],[])
209 splitEithers (e : es) = case e of
211 Right y -> (xs, y:ys)
212 where (xs,ys) = splitEithers es
215 A paranoid @zip@ (and some @zipWith@ friends) that checks the lists
216 are of equal length. Alastair Reid thinks this should only happen if
217 DEBUGging on; hey, why not?
220 zipEqual :: String -> [a] -> [b] -> [(a,b)]
221 zipWithEqual :: String -> (a->b->c) -> [a]->[b]->[c]
222 zipWith3Equal :: String -> (a->b->c->d) -> [a]->[b]->[c]->[d]
223 zipWith4Equal :: String -> (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e]
227 zipWithEqual _ = zipWith
228 zipWith3Equal _ = zipWith3
229 zipWith4Equal _ = zipWith4
231 zipEqual _ [] [] = []
232 zipEqual msg (a:as) (b:bs) = (a,b) : zipEqual msg as bs
233 zipEqual msg _ _ = panic ("zipEqual: unequal lists:"++msg)
235 zipWithEqual msg z (a:as) (b:bs)= z a b : zipWithEqual msg z as bs
236 zipWithEqual _ _ [] [] = []
237 zipWithEqual msg _ _ _ = panic ("zipWithEqual: unequal lists:"++msg)
239 zipWith3Equal msg z (a:as) (b:bs) (c:cs)
240 = z a b c : zipWith3Equal msg z as bs cs
241 zipWith3Equal _ _ [] [] [] = []
242 zipWith3Equal msg _ _ _ _ = panic ("zipWith3Equal: unequal lists:"++msg)
244 zipWith4Equal msg z (a:as) (b:bs) (c:cs) (d:ds)
245 = z a b c d : zipWith4Equal msg z as bs cs ds
246 zipWith4Equal _ _ [] [] [] [] = []
247 zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists:"++msg)
252 -- | 'zipLazy' is a kind of 'zip' that is lazy in the second list (observe the ~)
253 zipLazy :: [a] -> [b] -> [(a,b)]
255 -- We want to write this, but with GHC 6.4 we get a warning, so it
257 -- zipLazy (x:xs) ~(y:ys) = (x,y) : zipLazy xs ys
258 -- so we write this instead:
259 zipLazy (x:xs) zs = let y : ys = zs
260 in (x,y) : zipLazy xs ys
265 stretchZipWith :: (a -> Bool) -> b -> (a->b->c) -> [a] -> [b] -> [c]
266 -- ^ @stretchZipWith p z f xs ys@ stretches @ys@ by inserting @z@ in
267 -- the places where @p@ returns @True@
269 stretchZipWith _ _ _ [] _ = []
270 stretchZipWith p z f (x:xs) ys
271 | p x = f x z : stretchZipWith p z f xs ys
272 | otherwise = case ys of
274 (y:ys) -> f x y : stretchZipWith p z f xs ys
279 mapFst :: (a->c) -> [(a,b)] -> [(c,b)]
280 mapSnd :: (b->c) -> [(a,b)] -> [(a,c)]
282 mapFst f xys = [(f x, y) | (x,y) <- xys]
283 mapSnd f xys = [(x, f y) | (x,y) <- xys]
285 mapAndUnzip :: (a -> (b, c)) -> [a] -> ([b], [c])
287 mapAndUnzip _ [] = ([], [])
290 (rs1, rs2) = mapAndUnzip f xs
294 mapAndUnzip3 :: (a -> (b, c, d)) -> [a] -> ([b], [c], [d])
296 mapAndUnzip3 _ [] = ([], [], [])
297 mapAndUnzip3 f (x:xs)
298 = let (r1, r2, r3) = f x
299 (rs1, rs2, rs3) = mapAndUnzip3 f xs
301 (r1:rs1, r2:rs2, r3:rs3)
305 nOfThem :: Int -> a -> [a]
306 nOfThem n thing = replicate n thing
308 -- | @atLength atLen atEnd ls n@ unravels list @ls@ to position @n@. Precisely:
311 -- atLength atLenPred atEndPred ls n
312 -- | n < 0 = atLenPred n
313 -- | length ls < n = atEndPred (n - length ls)
314 -- | otherwise = atLenPred (drop n ls)
316 atLength :: ([a] -> b)
321 atLength atLenPred atEndPred ls n
322 | n < 0 = atEndPred n
323 | otherwise = go n ls
325 go n [] = atEndPred n
326 go 0 ls = atLenPred ls
327 go n (_:xs) = go (n-1) xs
329 -- Some special cases of atLength:
331 lengthExceeds :: [a] -> Int -> Bool
332 -- ^ > (lengthExceeds xs n) = (length xs > n)
333 lengthExceeds = atLength notNull (const False)
335 lengthAtLeast :: [a] -> Int -> Bool
336 lengthAtLeast = atLength notNull (== 0)
338 lengthIs :: [a] -> Int -> Bool
339 lengthIs = atLength null (==0)
341 listLengthCmp :: [a] -> Int -> Ordering
342 listLengthCmp = atLength atLen atEnd
346 | x > 0 = LT -- not yet seen 'n' elts, so list length is < n.
352 equalLength :: [a] -> [b] -> Bool
353 equalLength [] [] = True
354 equalLength (_:xs) (_:ys) = equalLength xs ys
355 equalLength _ _ = False
357 compareLength :: [a] -> [b] -> Ordering
358 compareLength [] [] = EQ
359 compareLength (_:xs) (_:ys) = compareLength xs ys
360 compareLength [] _ = LT
361 compareLength _ [] = GT
363 ----------------------------
364 singleton :: a -> [a]
367 isSingleton :: [a] -> Bool
368 isSingleton [_] = True
369 isSingleton _ = False
371 notNull :: [a] -> Bool
381 only _ = panic "Util: only"
384 Debugging/specialising versions of \tr{elem} and \tr{notElem}
387 isIn, isn'tIn :: Eq a => String -> a -> [a] -> Bool
390 isIn _msg x ys = elem__ x ys
391 isn'tIn _msg x ys = notElem__ x ys
393 --these are here to be SPECIALIZEd (automagically)
394 elem__ :: Eq a => a -> [a] -> Bool
396 elem__ x (y:ys) = x == y || elem__ x ys
398 notElem__ :: Eq a => a -> [a] -> Bool
399 notElem__ _ [] = True
400 notElem__ x (y:ys) = x /= y && notElem__ x ys
404 = elem (_ILIT(0)) x ys
408 | i ># _ILIT(100) = trace ("Over-long elem in " ++ msg)
409 (x `List.elem` (y:ys))
410 | otherwise = x == y || elem (i +# _ILIT(1)) x ys
413 = notElem (_ILIT(0)) x ys
415 notElem _ _ [] = True
417 | i ># _ILIT(100) = trace ("Over-long notElem in " ++ msg)
418 (x `List.notElem` (y:ys))
419 | otherwise = x /= y && notElem (i +# _ILIT(1)) x ys
423 %************************************************************************
425 \subsubsection[Utils-Carsten-mergesort]{A mergesort from Carsten}
427 %************************************************************************
430 Date: Mon, 3 May 93 20:45:23 +0200
431 From: Carsten Kehler Holst <kehler@cs.chalmers.se>
432 To: partain@dcs.gla.ac.uk
433 Subject: natural merge sort beats quick sort [ and it is prettier ]
435 Here is a piece of Haskell code that I'm rather fond of. See it as an
436 attempt to get rid of the ridiculous quick-sort routine. group is
437 quite useful by itself I think it was John's idea originally though I
438 believe the lazy version is due to me [surprisingly complicated].
439 gamma [used to be called] is called gamma because I got inspired by
440 the Gamma calculus. It is not very close to the calculus but does
441 behave less sequentially than both foldr and foldl. One could imagine
442 a version of gamma that took a unit element as well thereby avoiding
443 the problem with empty lists.
445 I've tried this code against
447 1) insertion sort - as provided by haskell
448 2) the normal implementation of quick sort
449 3) a deforested version of quick sort due to Jan Sparud
450 4) a super-optimized-quick-sort of Lennart's
452 If the list is partially sorted both merge sort and in particular
453 natural merge sort wins. If the list is random [ average length of
454 rising subsequences = approx 2 ] mergesort still wins and natural
455 merge sort is marginally beaten by Lennart's soqs. The space
456 consumption of merge sort is a bit worse than Lennart's quick sort
457 approx a factor of 2. And a lot worse if Sparud's bug-fix [see his
458 fpca article ] isn't used because of group.
465 group :: (a -> a -> Bool) -> [a] -> [[a]]
466 -- Given a <= function, group finds maximal contiguous up-runs
467 -- or down-runs in the input list.
468 -- It's stable, in the sense that it never re-orders equal elements
470 -- Date: Mon, 12 Feb 1996 15:09:41 +0000
471 -- From: Andy Gill <andy@dcs.gla.ac.uk>
472 -- Here is a `better' definition of group.
475 group p (x:xs) = group' xs x x (x :)
477 group' [] _ _ s = [s []]
478 group' (x:xs) x_min x_max s
479 | x_max `p` x = group' xs x_min x (s . (x :))
480 | not (x_min `p` x) = group' xs x x_max ((x :) . s)
481 | otherwise = s [] : group' xs x x (x :)
482 -- NB: the 'not' is essential for stablity
483 -- x `p` x_min would reverse equal elements
485 generalMerge :: (a -> a -> Bool) -> [a] -> [a] -> [a]
486 generalMerge _ xs [] = xs
487 generalMerge _ [] ys = ys
488 generalMerge p (x:xs) (y:ys) | x `p` y = x : generalMerge p xs (y:ys)
489 | otherwise = y : generalMerge p (x:xs) ys
491 -- gamma is now called balancedFold
493 balancedFold :: (a -> a -> a) -> [a] -> a
494 balancedFold _ [] = error "can't reduce an empty list using balancedFold"
495 balancedFold _ [x] = x
496 balancedFold f l = balancedFold f (balancedFold' f l)
498 balancedFold' :: (a -> a -> a) -> [a] -> [a]
499 balancedFold' f (x:y:xs) = f x y : balancedFold' f xs
500 balancedFold' _ xs = xs
502 generalNaturalMergeSort :: (a -> a -> Bool) -> [a] -> [a]
503 generalNaturalMergeSort _ [] = []
504 generalNaturalMergeSort p xs = (balancedFold (generalMerge p) . group p) xs
507 generalMergeSort p [] = []
508 generalMergeSort p xs = (balancedFold (generalMerge p) . map (: [])) xs
510 mergeSort, naturalMergeSort :: Ord a => [a] -> [a]
512 mergeSort = generalMergeSort (<=)
513 naturalMergeSort = generalNaturalMergeSort (<=)
515 mergeSortLe le = generalMergeSort le
518 sortLe :: (a->a->Bool) -> [a] -> [a]
519 sortLe le = generalNaturalMergeSort le
521 sortWith :: Ord b => (a->b) -> [a] -> [a]
522 sortWith get_key xs = sortLe le xs
524 x `le` y = get_key x < get_key y
526 on :: (a -> a -> Ordering) -> (b -> a) -> b -> b -> Ordering
527 on cmp sel = \x y -> sel x `cmp` sel y
531 %************************************************************************
533 \subsection[Utils-transitive-closure]{Transitive closure}
535 %************************************************************************
537 This algorithm for transitive closure is straightforward, albeit quadratic.
540 transitiveClosure :: (a -> [a]) -- Successor function
541 -> (a -> a -> Bool) -- Equality predicate
543 -> [a] -- The transitive closure
545 transitiveClosure succ eq xs
549 go done (x:xs) | x `is_in` done = go done xs
550 | otherwise = go (x:done) (succ x ++ xs)
553 x `is_in` (y:ys) | eq x y = True
554 | otherwise = x `is_in` ys
557 %************************************************************************
559 \subsection[Utils-accum]{Accumulating}
561 %************************************************************************
563 A combination of foldl with zip. It works with equal length lists.
566 foldl2 :: (acc -> a -> b -> acc) -> acc -> [a] -> [b] -> acc
568 foldl2 k z (a:as) (b:bs) = foldl2 k (k z a b) as bs
569 foldl2 _ _ _ _ = panic "Util: foldl2"
571 all2 :: (a -> b -> Bool) -> [a] -> [b] -> Bool
572 -- True if the lists are the same length, and
573 -- all corresponding elements satisfy the predicate
575 all2 p (x:xs) (y:ys) = p x y && all2 p xs ys
579 Count the number of times a predicate is true
582 count :: (a -> Bool) -> [a] -> Int
584 count p (x:xs) | p x = 1 + count p xs
585 | otherwise = count p xs
588 @splitAt@, @take@, and @drop@ but with length of another
589 list giving the break-off point:
592 takeList :: [b] -> [a] -> [a]
597 (y:ys) -> y : takeList xs ys
599 dropList :: [b] -> [a] -> [a]
601 dropList _ xs@[] = xs
602 dropList (_:xs) (_:ys) = dropList xs ys
605 splitAtList :: [b] -> [a] -> ([a], [a])
606 splitAtList [] xs = ([], xs)
607 splitAtList _ xs@[] = (xs, xs)
608 splitAtList (_:xs) (y:ys) = (y:ys', ys'')
610 (ys', ys'') = splitAtList xs ys
612 -- drop from the end of a list
613 dropTail :: Int -> [a] -> [a]
614 dropTail n = reverse . drop n . reverse
616 snocView :: [a] -> Maybe ([a],a)
617 -- Split off the last element
618 snocView [] = Nothing
619 snocView xs = go [] xs
621 -- Invariant: second arg is non-empty
622 go acc [x] = Just (reverse acc, x)
623 go acc (x:xs) = go (x:acc) xs
624 go _ [] = panic "Util: snocView"
626 split :: Char -> String -> [String]
627 split c s = case rest of
629 _:rest -> chunk : split c rest
630 where (chunk, rest) = break (==c) s
634 %************************************************************************
636 \subsection[Utils-comparison]{Comparisons}
638 %************************************************************************
641 isEqual :: Ordering -> Bool
642 -- Often used in (isEqual (a `compare` b))
647 thenCmp :: Ordering -> Ordering -> Ordering
648 {-# INLINE thenCmp #-}
649 thenCmp EQ ordering = ordering
650 thenCmp ordering _ = ordering
652 eqListBy :: (a->a->Bool) -> [a] -> [a] -> Bool
653 eqListBy _ [] [] = True
654 eqListBy eq (x:xs) (y:ys) = eq x y && eqListBy eq xs ys
655 eqListBy _ _ _ = False
657 cmpList :: (a -> a -> Ordering) -> [a] -> [a] -> Ordering
658 -- `cmpList' uses a user-specified comparer
663 cmpList cmp (a:as) (b:bs)
664 = case cmp a b of { EQ -> cmpList cmp as bs; xxx -> xxx }
668 -- This (with a more general type) is Data.List.stripPrefix from GHC 6.8.
669 -- This definition can be removed once we require at least 6.8 to build.
670 maybePrefixMatch :: String -> String -> Maybe String
671 maybePrefixMatch [] rest = Just rest
672 maybePrefixMatch (_:_) [] = Nothing
673 maybePrefixMatch (p:pat) (r:rest)
674 | p == r = maybePrefixMatch pat rest
675 | otherwise = Nothing
677 removeSpaces :: String -> String
678 removeSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace
681 %************************************************************************
683 \subsection[Utils-pairs]{Pairs}
685 %************************************************************************
688 unzipWith :: (a -> b -> c) -> [(a, b)] -> [c]
689 unzipWith f pairs = map ( \ (a, b) -> f a b ) pairs
693 seqList :: [a] -> b -> b
695 seqList (x:xs) b = x `seq` seqList xs b
701 global :: a -> IORef a
702 global a = unsafePerformIO (newIORef a)
706 consIORef :: IORef [a] -> a -> IO ()
709 writeIORef var (x:xs)
715 looksLikeModuleName :: String -> Bool
716 looksLikeModuleName [] = False
717 looksLikeModuleName (c:cs) = isUpper c && go cs
719 go ('.':cs) = looksLikeModuleName cs
720 go (c:cs) = (isAlphaNum c || c == '_') && go cs
723 Akin to @Prelude.words@, but acts like the Bourne shell, treating
724 quoted strings as Haskell Strings, and also parses Haskell [String]
728 getCmd :: String -> Either String -- Error
729 (String, String) -- (Cmd, Rest)
730 getCmd s = case break isSpace $ dropWhile isSpace s of
731 ([], _) -> Left ("Couldn't find command in " ++ show s)
734 toCmdArgs :: String -> Either String -- Error
735 (String, [String]) -- (Cmd, Args)
736 toCmdArgs s = case getCmd s of
738 Right (cmd, s') -> case toArgs s' of
740 Right args -> Right (cmd, args)
742 toArgs :: String -> Either String -- Error
745 = case dropWhile isSpace str of
746 s@('[':_) -> case reads s of
748 | all isSpace spaces ->
751 Left ("Couldn't read " ++ show str ++ "as [String]")
754 toArgs' s = case dropWhile isSpace s of
756 ('"' : _) -> case reads s of
758 -- rest must either be [] or start with a space
759 | all isSpace (take 1 rest) ->
762 Right args -> Right (arg : args)
764 Left ("Couldn't read " ++ show s ++ "as String")
765 s' -> case break isSpace s' of
766 (arg, s'') -> case toArgs' s'' of
768 Right args -> Right (arg : args)
771 -- -----------------------------------------------------------------------------
775 readRational__ :: ReadS Rational -- NB: doesn't handle leading "-"
776 readRational__ r = do
779 return ((n%1)*10^^(k-d), t)
782 (ds,s) <- lexDecDigits r
783 (ds',t) <- lexDotDigits s
784 return (read (ds++ds'), length ds', t)
786 readExp (e:s) | e `elem` "eE" = readExp' s
787 readExp s = return (0,s)
789 readExp' ('+':s) = readDec s
790 readExp' ('-':s) = do (k,t) <- readDec s
792 readExp' s = readDec s
795 (ds,r) <- nonnull isDigit s
796 return (foldl1 (\n d -> n * 10 + d) [ ord d - ord '0' | d <- ds ],
799 lexDecDigits = nonnull isDigit
801 lexDotDigits ('.':s) = return (span isDigit s)
802 lexDotDigits s = return ("",s)
804 nonnull p s = do (cs@(_:_),t) <- return (span p s)
807 readRational :: String -> Rational -- NB: *does* handle a leading "-"
810 '-' : xs -> - (read_me xs)
814 = case (do { (x,"") <- readRational__ s ; return x }) of
816 [] -> error ("readRational: no parse:" ++ top_s)
817 _ -> error ("readRational: ambiguous parse:" ++ top_s)
820 -----------------------------------------------------------------------------
821 -- Create a hierarchy of directories
823 createDirectoryHierarchy :: FilePath -> IO ()
824 createDirectoryHierarchy dir | isDrive dir = return () -- XXX Hack
825 createDirectoryHierarchy dir = do
826 b <- doesDirectoryExist dir
827 unless b $ do createDirectoryHierarchy (takeDirectory dir)
830 -----------------------------------------------------------------------------
831 -- Verify that the 'dirname' portion of a FilePath exists.
833 doesDirNameExist :: FilePath -> IO Bool
834 doesDirNameExist fpath = case takeDirectory fpath of
835 "" -> return True -- XXX Hack
836 _ -> doesDirectoryExist (takeDirectory fpath)
838 -- --------------------------------------------------------------
839 -- check existence & modification time at the same time
841 modificationTimeIfExists :: FilePath -> IO (Maybe ClockTime)
842 modificationTimeIfExists f = do
843 (do t <- getModificationTime f; return (Just t))
844 `IO.catch` \e -> if isDoesNotExistError e
848 -- split a string at the last character where 'pred' is True,
849 -- returning a pair of strings. The first component holds the string
850 -- up (but not including) the last character for which 'pred' returned
851 -- True, the second whatever comes after (but also not including the
854 -- If 'pred' returns False for all characters in the string, the original
855 -- string is returned in the first component (and the second one is just
857 splitLongestPrefix :: String -> (Char -> Bool) -> (String,String)
858 splitLongestPrefix str pred
859 | null r_pre = (str, [])
860 | otherwise = (reverse (tail r_pre), reverse r_suf)
861 -- 'tail' drops the char satisfying 'pred'
862 where (r_suf, r_pre) = break pred (reverse str)
864 escapeSpaces :: String -> String
865 escapeSpaces = foldr (\c s -> if isSpace c then '\\':c:s else c:s) ""
869 --------------------------------------------------------------
871 --------------------------------------------------------------
873 -- | The function splits the given string to substrings
874 -- using the 'searchPathSeparator'.
875 parseSearchPath :: String -> [FilePath]
876 parseSearchPath path = split path
878 split :: String -> [String]
882 _:rest -> chunk : split rest
886 #ifdef mingw32_HOST_OS
887 ('\"':xs@(_:_)) | last xs == '\"' -> init xs
891 (chunk', rest') = break isSearchPathSeparator s
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