| 1 | {-
|
| 2 | Copyright (C) 2006-2008 John Goerzen <jgoerzen@complete.org>
|
| 3 |
|
| 4 | This program is distributed in the hope that it will be useful, but
|
| 5 | WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 6 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
| 7 | General Public License for more details.
|
| 8 | -}
|
| 9 |
|
| 10 | {- |
|
| 11 | Module : Main
|
| 12 | Copyright : Copyright (C) 2008 John Goerzen
|
| 13 | License : GNU GPL, version 3 or above; see COPYRIGHT for details
|
| 14 |
|
| 15 | Maintainer : John Goerzen <jgoerzen@complete.org>
|
| 16 | Stability : provisional
|
| 17 | Portability: portable
|
| 18 |
|
| 19 | Written by John Goerzen, jgoerzen\@complete.org
|
| 20 |
|
| 21 | -}
|
| 22 |
|
| 23 | import System.Log.Logger
|
| 24 | import System.Log.Handler.Simple
|
| 25 | import System.IO(stderr)
|
| 26 | import System.Console.GetOpt.Utils
|
| 27 | import System.Console.GetOpt
|
| 28 | import System.Environment
|
| 29 | import Data.Quantity
|
| 30 | import Data.List
|
| 31 | import System.Exit
|
| 32 | import Control.Monad
|
| 33 | import Types
|
| 34 | import Scan
|
| 35 | import Data.List.Utils(split)
|
| 36 | import Actions
|
| 37 |
|
| 38 | main :: IO ()
|
| 39 | main =
|
| 40 | do updateGlobalLogger "" (setLevel INFO)
|
| 41 | argv <- getArgs
|
| 42 | case getOpt RequireOrder options argv of
|
| 43 | (o, n, []) -> worker o n
|
| 44 | (_, _, errors) -> usageerror (concat errors) -- ++ usageInfo header options)
|
| 45 |
|
| 46 | options :: [OptDescr (String, String)]
|
| 47 | options = [
|
| 48 | Option "0" ["null"] (NoArg ("0", ""))
|
| 49 | "Input items terminated by null character",
|
| 50 | Option "a" ["action"] (ReqArg (stdRequired "a") "ACTION")
|
| 51 | "Give action for output. Options are:\n\
|
| 52 | \print print each record with a newline\n\
|
| 53 | \ after [default]\n\
|
| 54 | \printfull print one line for each bin\n\
|
| 55 | \print0 print each record with NULL after\n\
|
| 56 | \exec:CMD Execute CMD in the shell for each\n\
|
| 57 | \record\n\
|
| 58 | \hardlink Hard link items into bins\n\
|
| 59 | \symlink Symlink items into bins",
|
| 60 | Option "b" ["binfmt"] (ReqArg (stdRequired "b") "FORMAT")
|
| 61 | "Gives bin name format in printf style.\n\
|
| 62 | \Tip: this can include a directory.\n\
|
| 63 | \default: %03d",
|
| 64 | Option "d" ["debug"] (NoArg ("d", "")) "Enable debugging",
|
| 65 | Option "D" ["deep-links"] (NoArg ("D", "")) "Enable deep bin directories",
|
| 66 | Option "p" ["preserve-order"] (NoArg ("p", ""))
|
| 67 | "Don't reorder files for maximum packing",
|
| 68 | Option "s" ["size"] (ReqArg (stdRequired "s") "SIZE")
|
| 69 | "Size of each output bin",
|
| 70 | Option "S" ["size-first"] (ReqArg (stdRequired "S") "SIZE")
|
| 71 | "Override size of first output bin",
|
| 72 | Option "" ["sort"] (NoArg ("sort", "")) "Sort input; useless without -p",
|
| 73 | Option "" ["help"] (NoArg ("help", "")) "Display this help"]
|
| 74 |
|
| 75 | worker :: [(String, String)] -> [FilePath] -> IO ()
|
| 76 | worker args files =
|
| 77 | do when (lookup "help" args == Just "") $ usageerror ""
|
| 78 | when (lookup "d" args == Just "")
|
| 79 | (updateGlobalLogger "" (setLevel DEBUG))
|
| 80 | handler <- streamHandler stderr DEBUG
|
| 81 | updateGlobalLogger "" (setHandlers [handler])
|
| 82 |
|
| 83 | runinfo <- case parseArgs args of
|
| 84 | Left x -> usageerror x
|
| 85 | Right x -> return x
|
| 86 |
|
| 87 | when (files == [])
|
| 88 | (usageerror "One or more files, or \"-\", must be specified")
|
| 89 |
|
| 90 | files_scan <- if files == ["-"]
|
| 91 | then readFileList (readNull runinfo)
|
| 92 | else return files
|
| 93 |
|
| 94 | let listToProc = if sortFiles runinfo then sort files_scan else files_scan
|
| 95 |
|
| 96 | results <- scan runinfo listToProc
|
| 97 | let numberedResults = zip [1..] (map (map snd) results)
|
| 98 | runAction runinfo numberedResults
|
| 99 |
|
| 100 | readFileList :: Bool -> IO [FilePath]
|
| 101 | readFileList nullsep =
|
| 102 | do c <- getContents
|
| 103 | return (splitfunc c)
|
| 104 | where splitfunc
|
| 105 | | nullsep = filter (/= "") . split "\0"
|
| 106 | | otherwise = lines
|
| 107 |
|
| 108 | parseArgs :: [(String, String)] -> Either String RunInfo
|
| 109 | parseArgs args =
|
| 110 | do size <- case lookup "s" args of
|
| 111 | Nothing -> fail "Missing required argument --size"
|
| 112 | Just x -> parseNumInt binaryOpts True x
|
| 113 | first <- case lookup "S" args of
|
| 114 | Nothing -> return size
|
| 115 | Just x -> parseNumInt binaryOpts True x
|
| 116 | let po = case lookup "p" args of
|
| 117 | Nothing -> False
|
| 118 | Just _ -> True
|
| 119 | let n = case lookup "0" args of
|
| 120 | Nothing -> False
|
| 121 | Just _ -> True
|
| 122 | let b = case lookup "b" args of
|
| 123 | Nothing -> "%03d"
|
| 124 | Just x -> x
|
| 125 | let deeplinks = case lookup "D" args of
|
| 126 | Nothing -> False
|
| 127 | Just _ -> True
|
| 128 | let dosort = case lookup "sort" args of
|
| 129 | Nothing -> False
|
| 130 | Just _ -> True
|
| 131 | a <- case lookup "a" args of
|
| 132 | Nothing -> return Print
|
| 133 | Just "print" -> return Print
|
| 134 | Just "printfull" -> return PrintFull
|
| 135 | Just "print0" -> return Print0
|
| 136 | Just "hardlink" -> return Hardlink
|
| 137 | Just "symlink" -> return Symlink
|
| 138 | Just x ->
|
| 139 | if "exec:" `isPrefixOf` x
|
| 140 | then return (Exec (drop 5 x))
|
| 141 | else fail $ "Unknown action: " ++ show x
|
| 142 | return $ RunInfo {binSize = size, firstBinSize = first,
|
| 143 | preserveOrder = po, readNull = n, binFmt = b,
|
| 144 | action = a, deepLinks = deeplinks, sortFiles = dosort}
|
| 145 |
|
| 146 | usageerror :: String -> IO t
|
| 147 | usageerror errormsg =
|
| 148 | do putStrLn errormsg
|
| 149 | putStrLn (usageInfo header options)
|
| 150 | putStrLn "If the single value \"-\" is given for inputfiles, the list of files"
|
| 151 | putStrLn "is read from stdin."
|
| 152 | exitFailure
|
| 153 |
|
| 154 | header :: String
|
| 155 | header = "\nUsage: datapacker [options] --size=n inputfiles\n\n\
|
| 156 | \Available options are:\n"
|