packages feed

Ebnf2ps-1.0.12: src/PathExpansion.hs

{-# LANGUAGE CPP #-}
module PathExpansion (expandPath)
where

#ifdef __GLASGOW_HASKELL__
import System.Environment (getEnv)
import System.Directory (getDirectoryContents, doesDirectoryExist)
import Control.Monad
import System.IO.Error (catchIOError, isDoesNotExistError)
import System.Posix.User (getUserEntryForName, homeDirectory)
#endif

expandPath :: [String] -> IO [String]

#if __GLASGOW_HASKELL__ || __HBC__

expandPath [] = return []
expandPath (path:paths) = do pathSpec1 <- replaceEnv(fst(stringToPathSpec([],path)))
			     pathSpec2 <- replaceHome(pathSpec1)
			     rest <- expandPath paths
			     beginn <- expandDirs(pSTS(rpBrace(replaceColon pathSpec2)))
			     return(beginn ++ rest)

-------------- Datenstruktur PathSpec -------------

data PathSpec =  EmptyPath|
		 Chars Char |
		 EnVar String |
		 Colon |
		 Tilde String|
		 Brace [PathSpec]|
		 Home String
		 deriving Show

------------- String in Datentyp PathSpec Parser ---------
stringToPathSpec :: ([PathSpec],String) -> ([PathSpec],[PathSpec])
stringToPathSpec (x,"") = (x++[EmptyPath],[EmptyPath])
stringToPathSpec (path,(x:xs))
	|x == '$' = stringToPathSpec (path ++ [EnVar (envName xs)],(envRest xs))
	|x == '{' = (path++[Brace (fst(stringToPathSpec([],xs)))]++snd(stringToPathSpec([],xs)),[EmptyPath])
	|x == '}' = (path++[EmptyPath],fst(stringToPathSpec ([],xs)))
	|x == ':' = stringToPathSpec (path ++[Colon],xs)
	|x == '~' && nextSign xs '/' = stringToPathSpec (path, "$HOME"++xs)
	|x == '~' = stringToPathSpec(path ++ [Home (envName xs)],envRest xs)
	|otherwise = stringToPathSpec (path ++ [Chars x],xs)


envName :: String -> String
envName "" = ""
envName (x:xs)
	|x == '{'  = takeWhile (not.(== '}')) xs
	|otherwise = takeWhile (not.specialSign) (x:xs)

envRest :: String -> String
envRest (x:xs)
	|x == '{'  = tail(dropWhile (not.(== '}')) xs)
	|otherwise = dropWhile (not.specialSign) (x:xs)

specialSign :: Char -> Bool
specialSign x = x == '/' || x == ':' || x == '{' || x == '}'


----- PathSpec, der nur aus Chars, in String ----------

pSTS :: [[PathSpec]] -> [String]
pSTS [] = []
pSTS (x:xs) = pathSpecToString x : pSTS xs
pathSpecToString :: [PathSpec] -> String
pathSpecToString [] = ""
pathSpecToString (Chars c : xs) = c:pathSpecToString xs
pathSpecToString (_:xs) = pathSpecToString xs
-------------------------------------------------------


-------- Ersetzen des Usernamen ----------
replaceHome :: [PathSpec] -> IO [PathSpec]
replaceHome [] = return []
replaceHome ((Home t):xs)= do user <- getUserEntryForName t
	   		      replaceHome(fst(stringToPathSpec([],homeDirectory user))++xs)
replaceHome ((Brace t):xs)= do braceIn <- replaceHome t
		               rest <- replaceHome xs
		               return(Brace(braceIn):(rest))
replaceHome (x:xs) = do t <- replaceHome xs
			return (x:t)


-------- Ersetzen der Umgebungsvariablen --------------

replaceEnv :: [PathSpec] -> IO [PathSpec]
replaceEnv [EmptyPath] = return []
replaceEnv ((EnVar t):xs)= do
			     name <- getEnv2 t
			     replaceEnv (fst(stringToPathSpec ([],name))++xs)
replaceEnv ((Brace t):xs) = do
                              braceIn <- replaceEnv t
			      rest <- replaceEnv xs
			      return (Brace (braceIn):(rest))
replaceEnv (x:xs) = do t <- replaceEnv xs
		       return (x:t)

getEnv2 t
	|t == "HOME" = catchIOError (getEnv t) (\e -> if isDoesNotExistError e then return ['.'] else ioError e)
	|otherwise = catchIOError (getEnv t) (\e -> if isDoesNotExistError e then return [] else ioError e)
-------------------------------------------------------

---------------- Teilen nach Doppelpunkt --------------
isColon :: PathSpec -> Bool
isColon Colon = True
isColon _ = False

replaceColon :: [PathSpec] -> [[PathSpec]]
replaceColon xs = let (l,s) = break (isColon) xs
	in l : case s of
		[] 	-> []
		(_:t) 	-> replaceColon t
-------------------------------------------------------

------- Ersetze Klammer -------------------------------

rpBrace :: [[PathSpec]] -> [[PathSpec]]
rpBrace [] = []
rpBrace (y:ys) = ((replaceBrace [] y) ++ (rpBrace ys))

replaceBrace :: [PathSpec] -> [PathSpec] -> [[PathSpec]]
replaceBrace dummy [] = [dummy]
replaceBrace dummy (Brace t : xs) = [foldr (++) [] (dummy:([a]++[b]))| q <- replaceColon t,a<- replaceBrace [] q, b <- replaceBrace [] xs]
replaceBrace dummy (x:xs) = replaceBrace (dummy ++ [x]) xs
-------------------------------------------------------
------- Subdirectory ----------------------------------

expandDirs :: [String] -> IO [String]
expandDirs [] = return []
expandDirs (path:paths) =
	do newpath <- expandOnePath [] path
	   newpaths <- expandDirs paths
	   return (newpath++newpaths)

expandOnePath :: String -> String -> IO [String]
expandOnePath anfang [] = return [anfang]
expandOnePath anfang (x:xs)
	|x == '/' && nextSign xs '/' =
		do allDirs <- expandAllSubPaths [(anfang ++ [x])]
		   const <- getConstraints (xs,[])
		   --expandDirs (map (++(fst(getConstraints (xs,[])))) [sub|sub <- allDirs, elemSub(reverse(snd(getConstraints (xs,[])))) (reverse sub)])
		   expandDirs (map (++ (fst const)) [sub|sub <- allDirs, elemSub(reverse(snd const)) (reverse sub)])
	|otherwise = do expandOnePath (anfang ++ [x]) xs


getConstraints :: (String,String) -> IO (String,String)
getConstraints ([],ys) = return ([],ys)
getConstraints (['/'],ys) = return ([],ys)
getConstraints ((x:xs),ys)
	|x == '/' && nextSign xs '/' = return ((x:xs),ys)
	|otherwise = getConstraints(xs,(ys ++ [x]))

elemSub :: String -> String -> Bool
elemSub [] _ = True
elemSub _ [] = False
elemSub (x:xs) (y:ys)
	|x == y = elemSub xs ys
	|otherwise = False

nextSign :: String -> Char -> Bool
nextSign [] _ = False
nextSign (x:xs) y = x == y

expandAllSubPaths :: [String] -> IO [String]
expandAllSubPaths [] = return []
expandAllSubPaths (x:xs) = do dirs <- searchForDirs (checkPath x)
		              subdirs <- expandAllSubPaths (dirs++xs)
		              return (x:subdirs)

searchForDirs :: FilePath -> IO [FilePath]
searchForDirs dir = do all_dir <- getDirectoryContents dir
		       dir_input <- filterM (specialPath) all_dir
	               filterM (doesDirectoryExist) (map (dir ++) dir_input)

specialPath :: FilePath -> IO Bool
specialPath p = return (special p)
  where special "." = False
	special ".." = False
	special _ = True

checkPath :: FilePath -> FilePath
checkPath path
	|last path == '/' = path
	|otherwise = path++"/"

----------------------------------------------------------------------------------
#else
-- for Hugs 1.4
expandPath = return

#endif