haskelltilde-expansion

Haskell get a specific user's home directory path


I want to make a function to expand names of the form ~user with that user's home directory (usually /home/user/), including ~root, which is typically /root/. I know of the getHomeDirectory method, to get the current user's home, but I don't know of any function to determine the home directory of a given user.

So far

I have the following function, which replaces a leading ~ in any file path with the user's home directory

-- join path with '/', except at root
-- opposite of breakPath
rejoinPath :: [FilePath] -> FilePath
rejoinPath []   = ""
rejoinPath (p:ps)
    | p == "/"  =  p ++ go ps
    | otherwise =  go (p:ps)
    where
        go :: [FilePath] -> FilePath
        go []     = ""
        go [p]    = p
        go (p:ps) = p ++ "/" ++ go ps

-- split path on '/', erasing separator except at root
-- opposite of rejoinPath
breakPath :: FilePath -> [FilePath]
breakPath []    = []
breakPath (c:cs)
    | c == '/'  = "/" : go "" cs
    | otherwise = go [c] cs
    where
        go :: FilePath -> FilePath -> [FilePath]
        go z []         = [z]
        go z (c:cs)
            | c == '/'  = z : go "" cs
            | otherwise = go (z ++ [c]) cs

expandHome :: FilePath -> IO FilePath
expandHome p = rejoinPath <$> (go $ breakPath p)
    where
        go []           = pure []
        go (p:ps)
            | p == "~"  = do 
                            home <- getHomeDirectory
                            pure $ home : ps
            | otherwise = pure $ p : ps

My understanding is that the getHomeDirectory reads from the HOME environment variable, and that the other home directories would be considerably harder to procure, but not impossible or prohibitively difficult I hope.

Note

I'm only interested in linux systems at the moment; I understand Windows and Mac have entirely different styles.


Solution

  • It looks like this is available in the unix package using getUserEntryForName and then the homeDirectory field of UserEntry.

    import System.Posix.User
    
    main :: IO ()
    main = do entry <- getUserEntryForName "djf"
              putStrLn (homeDirectory entry)