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, isDynamicGhcLib,
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,
34 fstOf3, sndOf3, thirdOf3,
36 -- * List operations controlled by another list
37 takeList, dropList, splitAtList, split,
51 -- * Transitive closures
60 -- * Argument processing
61 getCmd, toCmdArgs, toArgs,
67 createDirectoryHierarchy,
69 modificationTimeIfExists,
71 global, consIORef, globalMVar, globalEmptyMVar,
73 -- * Filenames and paths
78 Direction(..), reslash,
81 #include "HsVersions.h"
85 import Data.IORef ( IORef, newIORef, atomicModifyIORef )
86 import System.IO.Unsafe ( unsafePerformIO )
87 import Data.List hiding (group)
88 import Control.Concurrent.MVar ( MVar, newMVar, newEmptyMVar )
91 import qualified Data.List as List ( elem, 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
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, etc?}
111 %************************************************************************
113 These booleans are global constants, set by CPP flags. They allow us to
114 recompile a single module (this one) to change whether or not debug output
115 appears. They sometimes let us avoid even running CPP elsewhere.
117 It's important that the flags are literal constants (True/False). Then,
118 with -0, tests of the flags in other modules will simplify to the correct
119 branch of the conditional, thereby dropping debug code altogether when
123 ghciSupported :: Bool
127 ghciSupported = False
137 ghciTablesNextToCode :: Bool
138 #ifdef GHCI_TABLES_NEXT_TO_CODE
139 ghciTablesNextToCode = True
141 ghciTablesNextToCode = False
144 isDynamicGhcLib :: Bool
146 isDynamicGhcLib = True
148 isDynamicGhcLib = False
151 isWindowsHost :: Bool
152 #ifdef mingw32_HOST_OS
155 isWindowsHost = False
158 isWindowsTarget :: Bool
159 #ifdef mingw32_TARGET_OS
160 isWindowsTarget = True
162 isWindowsTarget = False
165 isDarwinTarget :: Bool
166 #ifdef darwin_TARGET_OS
167 isDarwinTarget = True
169 isDarwinTarget = False
173 %************************************************************************
175 \subsection{A for loop}
177 %************************************************************************
180 -- | Compose a function with itself n times. (nth rather than twice)
181 nTimes :: Int -> (a -> a) -> (a -> a)
184 nTimes n f = f . nTimes (n-1) f
188 fstOf3 :: (a,b,c) -> a
189 sndOf3 :: (a,b,c) -> b
190 thirdOf3 :: (a,b,c) -> c
196 %************************************************************************
198 \subsection[Utils-lists]{General list processing}
200 %************************************************************************
203 filterOut :: (a->Bool) -> [a] -> [a]
204 -- ^ Like filter, only it reverses the sense of the test
206 filterOut p (x:xs) | p x = filterOut p xs
207 | otherwise = x : filterOut p xs
209 partitionWith :: (a -> Either b c) -> [a] -> ([b], [c])
210 -- ^ Uses a function to determine which of two output lists an input element should join
211 partitionWith _ [] = ([],[])
212 partitionWith f (x:xs) = case f x of
214 Right c -> (bs, c:cs)
215 where (bs,cs) = partitionWith f xs
217 splitEithers :: [Either a b] -> ([a], [b])
218 -- ^ Teases a list of 'Either's apart into two lists
219 splitEithers [] = ([],[])
220 splitEithers (e : es) = case e of
222 Right y -> (xs, y:ys)
223 where (xs,ys) = splitEithers es
226 A paranoid @zip@ (and some @zipWith@ friends) that checks the lists
227 are of equal length. Alastair Reid thinks this should only happen if
228 DEBUGging on; hey, why not?
231 zipEqual :: String -> [a] -> [b] -> [(a,b)]
232 zipWithEqual :: String -> (a->b->c) -> [a]->[b]->[c]
233 zipWith3Equal :: String -> (a->b->c->d) -> [a]->[b]->[c]->[d]
234 zipWith4Equal :: String -> (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e]
238 zipWithEqual _ = zipWith
239 zipWith3Equal _ = zipWith3
240 zipWith4Equal _ = zipWith4
242 zipEqual _ [] [] = []
243 zipEqual msg (a:as) (b:bs) = (a,b) : zipEqual msg as bs
244 zipEqual msg _ _ = panic ("zipEqual: unequal lists:"++msg)
246 zipWithEqual msg z (a:as) (b:bs)= z a b : zipWithEqual msg z as bs
247 zipWithEqual _ _ [] [] = []
248 zipWithEqual msg _ _ _ = panic ("zipWithEqual: unequal lists:"++msg)
250 zipWith3Equal msg z (a:as) (b:bs) (c:cs)
251 = z a b c : zipWith3Equal msg z as bs cs
252 zipWith3Equal _ _ [] [] [] = []
253 zipWith3Equal msg _ _ _ _ = panic ("zipWith3Equal: unequal lists:"++msg)
255 zipWith4Equal msg z (a:as) (b:bs) (c:cs) (d:ds)
256 = z a b c d : zipWith4Equal msg z as bs cs ds
257 zipWith4Equal _ _ [] [] [] [] = []
258 zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists:"++msg)
263 -- | 'zipLazy' is a kind of 'zip' that is lazy in the second list (observe the ~)
264 zipLazy :: [a] -> [b] -> [(a,b)]
266 -- We want to write this, but with GHC 6.4 we get a warning, so it
268 -- zipLazy (x:xs) ~(y:ys) = (x,y) : zipLazy xs ys
269 -- so we write this instead:
270 zipLazy (x:xs) zs = let y : ys = zs
271 in (x,y) : zipLazy xs ys
276 stretchZipWith :: (a -> Bool) -> b -> (a->b->c) -> [a] -> [b] -> [c]
277 -- ^ @stretchZipWith p z f xs ys@ stretches @ys@ by inserting @z@ in
278 -- the places where @p@ returns @True@
280 stretchZipWith _ _ _ [] _ = []
281 stretchZipWith p z f (x:xs) ys
282 | p x = f x z : stretchZipWith p z f xs ys
283 | otherwise = case ys of
285 (y:ys) -> f x y : stretchZipWith p z f xs ys
290 mapFst :: (a->c) -> [(a,b)] -> [(c,b)]
291 mapSnd :: (b->c) -> [(a,b)] -> [(a,c)]
293 mapFst f xys = [(f x, y) | (x,y) <- xys]
294 mapSnd f xys = [(x, f y) | (x,y) <- xys]
296 mapAndUnzip :: (a -> (b, c)) -> [a] -> ([b], [c])
298 mapAndUnzip _ [] = ([], [])
301 (rs1, rs2) = mapAndUnzip f xs
305 mapAndUnzip3 :: (a -> (b, c, d)) -> [a] -> ([b], [c], [d])
307 mapAndUnzip3 _ [] = ([], [], [])
308 mapAndUnzip3 f (x:xs)
309 = let (r1, r2, r3) = f x
310 (rs1, rs2, rs3) = mapAndUnzip3 f xs
312 (r1:rs1, r2:rs2, r3:rs3)
316 nOfThem :: Int -> a -> [a]
317 nOfThem n thing = replicate n thing
319 -- | @atLength atLen atEnd ls n@ unravels list @ls@ to position @n@. Precisely:
322 -- atLength atLenPred atEndPred ls n
323 -- | n < 0 = atLenPred n
324 -- | length ls < n = atEndPred (n - length ls)
325 -- | otherwise = atLenPred (drop n ls)
327 atLength :: ([a] -> b)
332 atLength atLenPred atEndPred ls n
333 | n < 0 = atEndPred n
334 | otherwise = go n ls
336 go n [] = atEndPred n
337 go 0 ls = atLenPred ls
338 go n (_:xs) = go (n-1) xs
340 -- Some special cases of atLength:
342 lengthExceeds :: [a] -> Int -> Bool
343 -- ^ > (lengthExceeds xs n) = (length xs > n)
344 lengthExceeds = atLength notNull (const False)
346 lengthAtLeast :: [a] -> Int -> Bool
347 lengthAtLeast = atLength notNull (== 0)
349 lengthIs :: [a] -> Int -> Bool
350 lengthIs = atLength null (==0)
352 listLengthCmp :: [a] -> Int -> Ordering
353 listLengthCmp = atLength atLen atEnd
357 | x > 0 = LT -- not yet seen 'n' elts, so list length is < n.
363 equalLength :: [a] -> [b] -> Bool
364 equalLength [] [] = True
365 equalLength (_:xs) (_:ys) = equalLength xs ys
366 equalLength _ _ = False
368 compareLength :: [a] -> [b] -> Ordering
369 compareLength [] [] = EQ
370 compareLength (_:xs) (_:ys) = compareLength xs ys
371 compareLength [] _ = LT
372 compareLength _ [] = GT
374 ----------------------------
375 singleton :: a -> [a]
378 isSingleton :: [a] -> Bool
379 isSingleton [_] = True
380 isSingleton _ = False
382 notNull :: [a] -> Bool
392 only _ = panic "Util: only"
395 Debugging/specialising versions of \tr{elem} and \tr{notElem}
398 isIn, isn'tIn :: Eq a => String -> a -> [a] -> Bool
401 isIn _msg x ys = x `elem` ys
402 isn'tIn _msg x ys = x `notElem` ys
406 = elem100 (_ILIT(0)) x ys
408 elem100 _ _ [] = False
410 | i ># _ILIT(100) = trace ("Over-long elem in " ++ msg)
412 | otherwise = x == y || elem100 (i +# _ILIT(1)) x ys
415 = notElem100 (_ILIT(0)) x ys
417 notElem100 _ _ [] = True
418 notElem100 i x (y:ys)
419 | i ># _ILIT(100) = trace ("Over-long notElem in " ++ msg)
421 | otherwise = x /= y && notElem100 (i +# _ILIT(1)) x ys
425 %************************************************************************
427 \subsubsection[Utils-Carsten-mergesort]{A mergesort from Carsten}
429 %************************************************************************
432 Date: Mon, 3 May 93 20:45:23 +0200
433 From: Carsten Kehler Holst <kehler@cs.chalmers.se>
434 To: partain@dcs.gla.ac.uk
435 Subject: natural merge sort beats quick sort [ and it is prettier ]
437 Here is a piece of Haskell code that I'm rather fond of. See it as an
438 attempt to get rid of the ridiculous quick-sort routine. group is
439 quite useful by itself I think it was John's idea originally though I
440 believe the lazy version is due to me [surprisingly complicated].
441 gamma [used to be called] is called gamma because I got inspired by
442 the Gamma calculus. It is not very close to the calculus but does
443 behave less sequentially than both foldr and foldl. One could imagine
444 a version of gamma that took a unit element as well thereby avoiding
445 the problem with empty lists.
447 I've tried this code against
449 1) insertion sort - as provided by haskell
450 2) the normal implementation of quick sort
451 3) a deforested version of quick sort due to Jan Sparud
452 4) a super-optimized-quick-sort of Lennart's
454 If the list is partially sorted both merge sort and in particular
455 natural merge sort wins. If the list is random [ average length of
456 rising subsequences = approx 2 ] mergesort still wins and natural
457 merge sort is marginally beaten by Lennart's soqs. The space
458 consumption of merge sort is a bit worse than Lennart's quick sort
459 approx a factor of 2. And a lot worse if Sparud's bug-fix [see his
460 fpca article ] isn't used because of group.
467 group :: (a -> a -> Bool) -> [a] -> [[a]]
468 -- Given a <= function, group finds maximal contiguous up-runs
469 -- or down-runs in the input list.
470 -- It's stable, in the sense that it never re-orders equal elements
472 -- Date: Mon, 12 Feb 1996 15:09:41 +0000
473 -- From: Andy Gill <andy@dcs.gla.ac.uk>
474 -- Here is a `better' definition of group.
477 group p (x:xs) = group' xs x x (x :)
479 group' [] _ _ s = [s []]
480 group' (x:xs) x_min x_max s
481 | x_max `p` x = group' xs x_min x (s . (x :))
482 | not (x_min `p` x) = group' xs x x_max ((x :) . s)
483 | otherwise = s [] : group' xs x x (x :)
484 -- NB: the 'not' is essential for stablity
485 -- x `p` x_min would reverse equal elements
487 generalMerge :: (a -> a -> Bool) -> [a] -> [a] -> [a]
488 generalMerge _ xs [] = xs
489 generalMerge _ [] ys = ys
490 generalMerge p (x:xs) (y:ys) | x `p` y = x : generalMerge p xs (y:ys)
491 | otherwise = y : generalMerge p (x:xs) ys
493 -- gamma is now called balancedFold
495 balancedFold :: (a -> a -> a) -> [a] -> a
496 balancedFold _ [] = error "can't reduce an empty list using balancedFold"
497 balancedFold _ [x] = x
498 balancedFold f l = balancedFold f (balancedFold' f l)
500 balancedFold' :: (a -> a -> a) -> [a] -> [a]
501 balancedFold' f (x:y:xs) = f x y : balancedFold' f xs
502 balancedFold' _ xs = xs
504 generalNaturalMergeSort :: (a -> a -> Bool) -> [a] -> [a]
505 generalNaturalMergeSort _ [] = []
506 generalNaturalMergeSort p xs = (balancedFold (generalMerge p) . group p) xs
509 generalMergeSort p [] = []
510 generalMergeSort p xs = (balancedFold (generalMerge p) . map (: [])) xs
512 mergeSort, naturalMergeSort :: Ord a => [a] -> [a]
514 mergeSort = generalMergeSort (<=)
515 naturalMergeSort = generalNaturalMergeSort (<=)
517 mergeSortLe le = generalMergeSort le
520 sortLe :: (a->a->Bool) -> [a] -> [a]
521 sortLe le = generalNaturalMergeSort le
523 sortWith :: Ord b => (a->b) -> [a] -> [a]
524 sortWith get_key xs = sortLe le xs
526 x `le` y = get_key x < get_key y
528 on :: (a -> a -> c) -> (b -> a) -> b -> b -> c
529 on cmp sel = \x y -> sel x `cmp` sel y
533 %************************************************************************
535 \subsection[Utils-transitive-closure]{Transitive closure}
537 %************************************************************************
539 This algorithm for transitive closure is straightforward, albeit quadratic.
542 transitiveClosure :: (a -> [a]) -- Successor function
543 -> (a -> a -> Bool) -- Equality predicate
545 -> [a] -- The transitive closure
547 transitiveClosure succ eq xs
551 go done (x:xs) | x `is_in` done = go done xs
552 | otherwise = go (x:done) (succ x ++ xs)
555 x `is_in` (y:ys) | eq x y = True
556 | otherwise = x `is_in` ys
559 %************************************************************************
561 \subsection[Utils-accum]{Accumulating}
563 %************************************************************************
565 A combination of foldl with zip. It works with equal length lists.
568 foldl2 :: (acc -> a -> b -> acc) -> acc -> [a] -> [b] -> acc
570 foldl2 k z (a:as) (b:bs) = foldl2 k (k z a b) as bs
571 foldl2 _ _ _ _ = panic "Util: foldl2"
573 all2 :: (a -> b -> Bool) -> [a] -> [b] -> Bool
574 -- True if the lists are the same length, and
575 -- all corresponding elements satisfy the predicate
577 all2 p (x:xs) (y:ys) = p x y && all2 p xs ys
581 Count the number of times a predicate is true
584 count :: (a -> Bool) -> [a] -> Int
586 count p (x:xs) | p x = 1 + count p xs
587 | otherwise = count p xs
590 @splitAt@, @take@, and @drop@ but with length of another
591 list giving the break-off point:
594 takeList :: [b] -> [a] -> [a]
599 (y:ys) -> y : takeList xs ys
601 dropList :: [b] -> [a] -> [a]
603 dropList _ xs@[] = xs
604 dropList (_:xs) (_:ys) = dropList xs ys
607 splitAtList :: [b] -> [a] -> ([a], [a])
608 splitAtList [] xs = ([], xs)
609 splitAtList _ xs@[] = (xs, xs)
610 splitAtList (_:xs) (y:ys) = (y:ys', ys'')
612 (ys', ys'') = splitAtList xs ys
614 -- drop from the end of a list
615 dropTail :: Int -> [a] -> [a]
616 dropTail n = reverse . drop n . reverse
618 snocView :: [a] -> Maybe ([a],a)
619 -- Split off the last element
620 snocView [] = Nothing
621 snocView xs = go [] xs
623 -- Invariant: second arg is non-empty
624 go acc [x] = Just (reverse acc, x)
625 go acc (x:xs) = go (x:acc) xs
626 go _ [] = panic "Util: snocView"
628 split :: Char -> String -> [String]
629 split c s = case rest of
631 _:rest -> chunk : split c rest
632 where (chunk, rest) = break (==c) s
636 %************************************************************************
638 \subsection[Utils-comparison]{Comparisons}
640 %************************************************************************
643 isEqual :: Ordering -> Bool
644 -- Often used in (isEqual (a `compare` b))
649 thenCmp :: Ordering -> Ordering -> Ordering
650 {-# INLINE thenCmp #-}
651 thenCmp EQ ordering = ordering
652 thenCmp ordering _ = ordering
654 eqListBy :: (a->a->Bool) -> [a] -> [a] -> Bool
655 eqListBy _ [] [] = True
656 eqListBy eq (x:xs) (y:ys) = eq x y && eqListBy eq xs ys
657 eqListBy _ _ _ = False
659 cmpList :: (a -> a -> Ordering) -> [a] -> [a] -> Ordering
660 -- `cmpList' uses a user-specified comparer
665 cmpList cmp (a:as) (b:bs)
666 = case cmp a b of { EQ -> cmpList cmp as bs; xxx -> xxx }
670 removeSpaces :: String -> String
671 removeSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace
674 %************************************************************************
676 \subsection[Utils-pairs]{Pairs}
678 %************************************************************************
681 unzipWith :: (a -> b -> c) -> [(a, b)] -> [c]
682 unzipWith f pairs = map ( \ (a, b) -> f a b ) pairs
686 seqList :: [a] -> b -> b
688 seqList (x:xs) b = x `seq` seqList xs b
694 global :: a -> IORef a
695 global a = unsafePerformIO (newIORef a)
699 consIORef :: IORef [a] -> a -> IO ()
701 atomicModifyIORef var (\xs -> (x:xs,()))
705 globalMVar :: a -> MVar a
706 globalMVar a = unsafePerformIO (newMVar a)
708 globalEmptyMVar :: MVar a
709 globalEmptyMVar = unsafePerformIO newEmptyMVar
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