{-| hledger - a ledger-compatible accounting tool. Copyright (c) 2007-2011 Simon Michael Released under GPL version 3 or later. hledger is a partial haskell clone of John Wiegley's "ledger". It generates ledger-compatible register & balance reports from a plain text journal, and demonstrates a functional implementation of ledger. For more information, see http:\/\/hledger.org . This module provides the main function for the hledger command-line executable. It is exposed here so that it can be imported by eg benchmark scripts. You can use the command line: > $ hledger --help or ghci: > $ ghci hledger > > j <- readJournalFile Nothing Nothing "data/sample.journal" > > register [] ["income","expenses"] j > 2008/01/01 income income:salary $-1 $-1 > 2008/06/01 gift income:gifts $-1 $-2 > 2008/06/03 eat & shop expenses:food $1 $-1 > expenses:supplies $1 0 > > balance [Depth "1"] [] l > $-1 assets > $2 expenses > $-2 income > $1 liabilities > > l <- myLedger See "Hledger.Data.Ledger" for more examples. -} module Hledger.Cli.Main where import Control.Monad import Data.List import Safe import System.Console.CmdArgs.Explicit (modeHelp) -- import System.Console.CmdArgs.Helper import System.Environment import System.Exit import System.Process import Text.Printf import Hledger (ensureJournalFileExists) import Hledger.Cli.Add import Hledger.Cli.Balance import Hledger.Cli.Balancesheet import Hledger.Cli.Cashflow import Hledger.Cli.Histogram import Hledger.Cli.Incomestatement import Hledger.Cli.Print import Hledger.Cli.Register import Hledger.Cli.Stats import Hledger.Cli.Options import Hledger.Cli.Tests import Hledger.Cli.Utils import Hledger.Cli.Version import Hledger.Utils import Hledger.Reports import Hledger.Data.Dates main :: IO () main = do -- Choose and run the appropriate internal or external command based -- on the raw command-line arguments, cmdarg's interpretation of -- same, and hledger-* executables in the user's PATH. A somewhat -- complex mishmash of cmdargs and custom processing, hence all the -- debugging support and tests. See also Hledger.Cli.Options and -- command-line.test. -- some preliminary (imperfect) argument parsing to supplement cmdargs args <- getArgs let args' = moveFlagsAfterCommand args isFlag = ("-" `isPrefixOf`) isNonEmptyNonFlag s = not (isFlag s) && not (null s) rawcmd = headDef "" $ takeWhile isNonEmptyNonFlag args' isNullCommand = null rawcmd (argsbeforecmd, argsaftercmd') = break (==rawcmd) args argsaftercmd = drop 1 argsaftercmd' when (debugLevel > 0) $ do printf "running: %s\n" prognameandversion printf "raw args: %s\n" (show args) printf "raw args rearranged for cmdargs: %s\n" (show args') printf "raw command might be: %s\n" (show rawcmd) printf "raw args before command: %s\n" (show argsbeforecmd) printf "raw args after command: %s\n" (show argsaftercmd) -- search PATH for add-ons addons <- getHledgerAddonCommands -- parse arguments with cmdargs opts <- argsToCliOpts args addons -- select an action and run it. let cmd = command_ opts -- the full matched internal or external command name, if any isInternalCommand = not (null cmd) && not (cmd `elem` addons) -- probably isExternalCommand = not (null cmd) && cmd `elem` addons -- probably isBadCommand = not (null rawcmd) && null cmd hasHelp args = any (`elem` args) ["--help","-h","-?"] hasVersion = ("--version" `elem`) mainmode' = mainmode addons generalHelp = putStr $ showModeHelp mainmode' version = putStrLn prognameandversion badCommandError = error' ("command "++rawcmd++" is not recognized, run with no command to see a list") >> exitFailure f `orShowHelp` mode = if hasHelp args then putStr (showModeHelp mode) else f when (debug_ opts > 0) $ do putStrLn $ "processed opts:\n" ++ ppShow opts putStrLn $ "command matched: " ++ show cmd putStrLn $ "isNullCommand: " ++ show isNullCommand putStrLn $ "isInternalCommand: " ++ show isInternalCommand putStrLn $ "isExternalCommand: " ++ show isExternalCommand putStrLn $ "isBadCommand: " ++ show isBadCommand d <- getCurrentDay putStrLn $ "date span from opts: " ++ (show $ dateSpanFromOpts d $ reportopts_ opts) putStrLn $ "interval from opts: " ++ (show $ intervalFromOpts $ reportopts_ opts) putStrLn $ "query from opts & args: " ++ (show $ queryFromOpts d $ reportopts_ opts) let dbg s = if debug_ opts > 0 then trace s else id runHledgerCommand -- high priority flags and situations. --help should be highest priority. | hasHelp argsbeforecmd = dbg "--help before command, showing general help" generalHelp | not (hasHelp argsaftercmd) && (hasVersion argsbeforecmd || (hasVersion argsaftercmd && isInternalCommand)) = version -- \| (null externalcmd) && "binary-filename" `inRawOpts` rawopts = putStrLn $ binaryfilename progname -- \| "--browse-args" `elem` args = System.Console.CmdArgs.Helper.execute "cmdargs-browser" mainmode' args >>= (putStr . show) | isNullCommand = dbg "no command, showing general help" generalHelp | isBadCommand = badCommandError -- internal commands | cmd == "activity" = withJournalDo opts histogram `orShowHelp` activitymode | cmd == "add" = (journalFilePathFromOpts opts >>= ensureJournalFileExists >> withJournalDo opts add) `orShowHelp` addmode | cmd == "balance" = withJournalDo opts balance `orShowHelp` balancemode | cmd == "balancesheet" = withJournalDo opts balancesheet `orShowHelp` balancesheetmode | cmd == "cashflow" = withJournalDo opts cashflow `orShowHelp` cashflowmode | cmd == "incomestatement" = withJournalDo opts incomestatement `orShowHelp` incomestatementmode | cmd == "print" = withJournalDo opts print' `orShowHelp` printmode | cmd == "register" = withJournalDo opts register `orShowHelp` registermode | cmd == "stats" = withJournalDo opts stats `orShowHelp` statsmode | cmd == "test" = test' opts `orShowHelp` testmode -- an external command | isExternalCommand = do let shellcmd = printf "%s-%s %s" progname cmd (unwords' argsaftercmd) when (debug_ opts > 0) $ do printf "external command selected: %s\n" cmd printf "external command arguments: %s\n" (show argsaftercmd) printf "running shell command: %s\n" (show shellcmd) system shellcmd >>= exitWith -- deprecated commands | cmd == "convert" = error' (modeHelp convertmode) >> exitFailure -- shouldn't reach here | otherwise = optserror ("could not understand the arguments "++show args) >> exitFailure runHledgerCommand -- tests_runHledgerCommand = [ -- -- "runHledgerCommand" ~: do -- -- let opts = defreportopts{query_="expenses"} -- -- d <- getCurrentDay -- -- runHledgerCommand addons opts@CliOpts{command_=cmd} args -- ]