b7j0c.org


a haskell program for finding your new freebsd ports

newports.hs is a trivial haskell script that can be used to find what ports have been updated on your system in the last N days, which you would typically want to know after doing a daily cvsup. the instructions are easy - the script only takes one argument - a number signifying the age in days of ports you want displayed. if you wanted to use runhaskell utility, you could run it like:

runhaskell newports.hs 1

the code is under a bsd license.

here is a haskell HackageDB package for this script.

if you find bugs, errors, or general crapitude, let me know!

note - the code below has entities used in place of certain characters so that this page is valid xhtml. so don't cut and paste from it - use this link directly to the source

{-

this is a trivial script to tell us when a freebsd port has been updated 
within the last N days, where N is the argument provided to the script on the
command line.

this code is licensed under a "bsd" license, which is stated below

Copyright (c) 2007, Brad Clawsie. All rights reserved.
http://b7j0c.org/content/license.txt

-}

module Main (main) where
import qualified System.Directory as D 
    (getDirectoryContents,doesDirectoryExist,getModificationTime)
import qualified System.Time as T 
    (normalizeTimeDiff,diffClockTimes,getClockTime,tdSec,ClockTime(..))
import qualified System.Environment as S (getArgs)
import qualified System.Exit as E (exitWith,ExitCode(..))
import qualified Control.Monad as M (liftM,mapM,filterM)
import qualified Data.Char as C (isDigit)

main :: IO ()
main = do
  args <- S.getArgs
  let useMsg = "use: newports [age-in-days]" :: String
  case (length args == 1) of
    False -> error useMsg
    True -> 
        let rawArg = head args in
        case (all C.isDigit rawArg) of
          False -> error useMsg
          {- we will base our age comparisons on seconds, so we
             convert our arg to a day count as seconds -}
          True -> let age = 86400 * (read rawArg :: Int) in
                  do 
                    now <- T.getClockTime
                    let portsBase = "/usr/ports" :: FilePath
                    dirs <- portsDirs portsBase
                    allPorts <- M.mapM portsDirs dirs 
                    {- allPorts :: [[FilePath]] -}
                    allPortDates <- M.liftM concat 
                                    (M.mapM getModTimes allPorts) 
                    {- allPortDates:: [(FilePath,ClockTime)]
                       where the ClockTime indicates the port mtime -}
                    let newPorts = filter (isNewPort now age) allPortDates
                    case (length newPorts) of
                      0 -> E.exitWith E.ExitSuccess
                      _ -> do 
                            M.mapM putStrLn (map fst newPorts)
                            E.exitWith E.ExitSuccess
                  where                      
                    isNewPort :: T.ClockTime -> Int ->
                                 (FilePath,T.ClockTime) -> Bool
                    isNewPort now age (port,mtime) =
                        let diff = T.tdSec(T.diffClockTimes now mtime) in
                        diff <= age
                    portsDirs :: FilePath -> IO [FilePath]
                    portsDirs d = do
                            rawDirs <- D.getDirectoryContents d
                            l <- M.filterM (D.doesDirectoryExist) $
                                 map ((d ++ "/") ++) $
                                 filter (\x -> (head x) /= '.') rawDirs
                            return l
                    getModTimes :: [FilePath] -> IO [(FilePath,T.ClockTime)]
                    getModTimes d = do
                            t <- M.mapM D.getModificationTime d
                            return $ zip d t

last update 07/07/2007