{- Copyright (C) 2006-2008 John Goerzen This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -} {- | Module : Main Copyright : Copyright (C) 2008 John Goerzen License : GNU GPL, version 3 or above; see COPYRIGHT for details Maintainer : John Goerzen Stability : provisional Portability: portable Written by John Goerzen, jgoerzen\@complete.org -} import System.Log.Logger import System.Log.Handler.Simple import System.IO(stderr) import System.Console.GetOpt.Utils import System.Console.GetOpt import System.Environment import Data.Quantity import Data.List import System.Exit import Control.Monad import Types import Scan import Data.List.Utils(split) import Actions main :: IO () main = do updateGlobalLogger "" (setLevel INFO) argv <- getArgs case getOpt RequireOrder options argv of (o, n, []) -> worker o n (_, _, errors) -> usageerror (concat errors) -- ++ usageInfo header options) options :: [OptDescr (String, String)] options = [ Option "0" ["null"] (NoArg ("0", "")) "Input items terminated by null character", Option "a" ["action"] (ReqArg (stdRequired "a") "ACTION") "Give action for output. Options are:\n\ \print print each record with a newline\n\ \ after [default]\n\ \printfull print one line for each bin\n\ \print0 print each record with NULL after\n\ \exec:CMD Execute CMD in the shell for each\n\ \record\n\ \hardlink Hard link items into bins\n\ \symlink Symlink items into bins", Option "b" ["binfmt"] (ReqArg (stdRequired "b") "FORMAT") "Gives bin name format in printf style.\n\ \Tip: this can include a directory.\n\ \default: %03d", Option "d" ["debug"] (NoArg ("d", "")) "Enable debugging", Option "D" ["deep-links"] (NoArg ("D", "")) "Enable deep bin directories", Option "p" ["preserve-order"] (NoArg ("p", "")) "Don't reorder files for maximum packing", Option "s" ["size"] (ReqArg (stdRequired "s") "SIZE") "Size of each output bin", Option "S" ["size-first"] (ReqArg (stdRequired "S") "SIZE") "Override size of first output bin", Option "" ["sort"] (NoArg ("sort", "")) "Sort input; useless without -p", Option "" ["help"] (NoArg ("help", "")) "Display this help"] worker :: [(String, String)] -> [FilePath] -> IO () worker args files = do when (lookup "help" args == Just "") $ usageerror "" when (lookup "d" args == Just "") (updateGlobalLogger "" (setLevel DEBUG)) handler <- streamHandler stderr DEBUG updateGlobalLogger "" (setHandlers [handler]) runinfo <- case parseArgs args of Left x -> usageerror x Right x -> return x when (files == []) (usageerror "One or more files, or \"-\", must be specified") files_scan <- if files == ["-"] then readFileList (readNull runinfo) else return files let listToProc = if sortFiles runinfo then sort files_scan else files_scan results <- scan runinfo listToProc let numberedResults = zip [1..] (map (map snd) results) runAction runinfo numberedResults readFileList :: Bool -> IO [FilePath] readFileList nullsep = do c <- getContents return (splitfunc c) where splitfunc | nullsep = filter (/= "") . split "\0" | otherwise = lines parseArgs :: [(String, String)] -> Either String RunInfo parseArgs args = do size <- case lookup "s" args of Nothing -> fail "Missing required argument --size" Just x -> parseNumInt binaryOpts True x first <- case lookup "S" args of Nothing -> return size Just x -> parseNumInt binaryOpts True x let po = case lookup "p" args of Nothing -> False Just _ -> True let n = case lookup "0" args of Nothing -> False Just _ -> True let b = case lookup "b" args of Nothing -> "%03d" Just x -> x let deeplinks = case lookup "D" args of Nothing -> False Just _ -> True let dosort = case lookup "sort" args of Nothing -> False Just _ -> True a <- case lookup "a" args of Nothing -> return Print Just "print" -> return Print Just "printfull" -> return PrintFull Just "print0" -> return Print0 Just "hardlink" -> return Hardlink Just "symlink" -> return Symlink Just x -> if "exec:" `isPrefixOf` x then return (Exec (drop 5 x)) else fail $ "Unknown action: " ++ show x return $ RunInfo {binSize = size, firstBinSize = first, preserveOrder = po, readNull = n, binFmt = b, action = a, deepLinks = deeplinks, sortFiles = dosort} usageerror :: String -> IO t usageerror errormsg = do putStrLn errormsg putStrLn (usageInfo header options) putStrLn "If the single value \"-\" is given for inputfiles, the list of files" putStrLn "is read from stdin." exitFailure header :: String header = "\nUsage: datapacker [options] --size=n inputfiles\n\n\ \Available options are:\n"