diff --git a/hledger-chart/Hledger/Chart/Main.hs b/hledger-chart/Hledger/Chart/Main.hs index 43ec38fc6..5cfb82fc0 100644 --- a/hledger-chart/Hledger/Chart/Main.hs +++ b/hledger-chart/Hledger/Chart/Main.hs @@ -16,36 +16,59 @@ import Data.Colour.RGBSpace.HSL (hsl) import Data.Colour.SRGB.Linear (rgb) import Data.List import Safe (readDef) +import System.Console.GetOpt #if __GLASGOW_HASKELL__ <= 610 import Prelude hiding (putStr, putStrLn) import System.IO.UTF8 (putStr, putStrLn) #endif -import Hledger.Chart import Hledger.Cli.Commands import Hledger.Cli.Options import Hledger.Cli.Tests import Hledger.Cli.Utils (withJournalDo) -import Hledger.Cli.Version (versionmsg, binaryfilename) +import Hledger.Cli.Version (progversionstr, binaryfilename) import Hledger.Data +progname_chart = progname_cli ++ "-chart" + +defchartoutput = "hledger.png" +defchartitems = 10 +defchartsize = "600x400" + +options_chart :: [OptDescr Opt] +options_chart = [ + Option "o" ["output"] (ReqArg ChartOutput "FILE") ("output filename (default: "++defchartoutput++")") + ,Option "" ["items"] (ReqArg ChartItems "N") ("number of accounts to show (default: "++show defchartitems++")") + ,Option "" ["size"] (ReqArg ChartSize "WIDTHxHEIGHT") ("image size (default: "++defchartsize++")") + ] + +usage_preamble_chart = + "Usage: hledger-chart [OPTIONS] [PATTERNS]\n" ++ + "\n" ++ + "Reads your ~/.journal file, or another specified by $LEDGER or -f, and\n" ++ + "generates simple pie chart images.\n" ++ + "\n" + +usage_options_chart = usageInfo "hledger-chart options:" options_chart ++ "\n" + +usage_chart = concat [ + usage_preamble_chart + ,usage_options_chart + ,usage_options_cli + ,usage_postscript_cli + ] + main :: IO () main = do - (opts, cmd, args) <- parseArguments - run cmd opts args + (opts, cmd, args) <- parseArgumentsWith (options_cli++options_chart) usage_chart + run opts (cmd:args) where - run cmd opts args - | Help `elem` opts = putStr help1 - | HelpOptions `elem` opts = putStr help2 - | HelpAll `elem` opts = putStr $ help1 ++ "\n" ++ help2 - | Version `elem` opts = putStrLn versionmsg - | BinaryFilename `elem` opts = putStrLn binaryfilename - | null cmd = maybe (putStr help1) (withJournalDo opts args cmd) defaultcmd - | cmd `isPrefixOf` "chart" = withJournalDo opts args cmd chart - | otherwise = putStr help1 - - defaultcmd = Just chart + run opts args + | Help `elem` opts = putStr usage_chart + | Version `elem` opts = putStrLn $ progversionstr progname_chart + | BinaryFilename `elem` opts = putStrLn $ binaryfilename progname_chart + | otherwise = withJournalDo opts args "chart" chart -- | Generate an image with the pie chart and write it to a file chart :: [Opt] -> [String] -> Journal -> IO () @@ -54,8 +77,8 @@ chart opts args j = do let chart = genPie opts (optsToFilterSpec opts args t) j renderableToPNGFile (toRenderable chart) w h filename where - filename = getOption opts ChartOutput chartoutput - (w,h) = parseSize $ getOption opts ChartSize chartsize + filename = getOption opts ChartOutput defchartoutput + (w,h) = parseSize $ getOption opts ChartSize defchartsize -- | Extract string option value from a list of options or use the default getOption :: [Opt] -> (String->Opt) -> String -> String @@ -89,7 +112,7 @@ genPie opts filterspec j = defaultPieLayout { pie_background_ = solidFillStyle $ where (topn,rest) = splitAt n $ reverse $ sortBy (comparing snd) t other = ("other", sum $ map snd rest) - num = readDef (fromIntegral chartitems) (getOption opts ChartItems (show chartitems)) + num = readDef (fromIntegral defchartitems) (getOption opts ChartItems (show defchartitems)) hue = if sign > 0 then red else green where (red, green) = (0, 110) debug s = if Debug `elem` opts then ltrace s else id diff --git a/hledger-vty/Hledger/Vty/Main.hs b/hledger-vty/Hledger/Vty/Main.hs index 787c3e13f..cd874e0ca 100644 --- a/hledger-vty/Hledger/Vty/Main.hs +++ b/hledger-vty/Hledger/Vty/Main.hs @@ -11,34 +11,52 @@ module Hledger.Vty.Main where import Prelude hiding (putStr, putStrLn) import System.IO.UTF8 (putStr, putStrLn) #endif -import Safe (headDef) import Graphics.Vty +import Safe (headDef) +import System.Console.GetOpt import Hledger.Cli.Balance import Hledger.Cli.Options import Hledger.Cli.Print import Hledger.Cli.Register import Hledger.Cli.Utils (withJournalDo) -import Hledger.Cli.Version (versionmsg, binaryfilename) +import Hledger.Cli.Version (progversionstr, binaryfilename) import Hledger.Data +progname_vty = progname_cli ++ "-vty" + +options_vty :: [OptDescr Opt] +options_vty = [ + Option "" ["debug-vty"] (NoArg DebugVty) "run with no terminal output, showing console" + ] + +usage_preamble_vty = + "Usage: hledger-vty [OPTIONS] [PATTERNS]\n" ++ + "\n" ++ + "Reads your ~/.journal file, or another specified by $LEDGER or -f, and\n" ++ + "starts the full-window curses ui.\n" ++ + "\n" + +usage_options_vty = usageInfo "hledger-vty options:" options_vty ++ "\n" + +usage_vty = concat [ + usage_preamble_vty + ,usage_options_vty + ,usage_options_cli + ,usage_postscript_cli + ] + main :: IO () main = do - (opts, cmd, args) <- parseArguments - run cmd opts args + (opts, cmd, args) <- parseArgumentsWith (options_cli++options_vty) usage_vty + run opts (cmd:args) where - run cmd opts args - | Help `elem` opts = putStr help1 - | HelpOptions `elem` opts = putStr help2 - | HelpAll `elem` opts = putStr $ help1 ++ "\n" ++ help2 - | Version `elem` opts = putStrLn versionmsg - | BinaryFilename `elem` opts = putStrLn binaryfilename - | null cmd = maybe (putStr help1) (withJournalDo opts args cmd) defaultcmd - | cmd `isPrefixOf` "vty" = withJournalDo opts args cmd vty - | otherwise = putStr help1 - - defaultcmd = Just vty + run opts args + | Help `elem` opts = putStr usage_vty + | Version `elem` opts = putStrLn $ progversionstr progname_vty + | BinaryFilename `elem` opts = putStrLn $ binaryfilename progname_vty + | otherwise = withJournalDo opts args "vty" vty helpmsg = "(b)alance, (r)egister, (p)rint, (right) to drill down, (left) to back up, (q)uit" diff --git a/hledger-web/Hledger/Web/Main.hs b/hledger-web/Hledger/Web/Main.hs index dcc732b12..dc5d204f7 100644 --- a/hledger-web/Hledger/Web/Main.hs +++ b/hledger-web/Hledger/Web/Main.hs @@ -13,37 +13,55 @@ import System.IO.UTF8 (putStr, putStrLn) #endif import Control.Concurrent (forkIO, threadDelay) import Network.Wai.Handler.SimpleServer (run) -import System.Exit (exitFailure) -- , exitWith, ExitCode(ExitSuccess)) -- base 3 compatible +import System.Exit (exitFailure) import System.IO.Storage (withStore, putValue,) import Yesod.Content (typeByExt) import Yesod.Helpers.Static (fileLookupDir) +import System.Console.GetOpt import Hledger.Cli.Options import Hledger.Cli.Utils (withJournalDo, openBrowserOn) -import Hledger.Cli.Version (versionmsg) --, binaryfilename) +import Hledger.Cli.Version (progversionstr, binaryfilename) import Hledger.Data import Hledger.Web.App (App(..), withApp) import Hledger.Web.Files (createFilesIfMissing) import Hledger.Web.Settings (browserstartdelay, defhost, defport, datadir) +progname_web = progname_cli ++ "-web" + +options_web :: [OptDescr Opt] +options_web = [ + Option "" ["base-url"] (ReqArg BaseUrl "URL") "use this base url (default http://localhost:PORT)" + ,Option "" ["port"] (ReqArg Port "N") "serve on tcp port N (default 5000)" + ] + +usage_preamble_web = + "Usage: hledger-web [OPTIONS] [PATTERNS]\n" ++ + "\n" ++ + "Reads your ~/.journal file, or another specified by $LEDGER or -f, and\n" ++ + "starts a web ui server. Also attempts to start a web browser (unless --debug).\n" ++ + "\n" + +usage_options_web = usageInfo "hledger-web options:" options_web ++ "\n" + +usage_web = concat [ + usage_preamble_web + ,usage_options_web + ,usage_options_cli + ,usage_postscript_cli + ] + main :: IO () main = do - (opts, cmd, args) <- parseArguments - run cmd opts args + (opts, cmd, args) <- parseArgumentsWith (options_cli++options_web) usage_web + run opts (cmd:args) where - run cmd opts args - | Help `elem` opts = putStr help1 - | HelpOptions `elem` opts = putStr help2 - | HelpAll `elem` opts = putStr $ help1 ++ "\n" ++ help2 - | Version `elem` opts = putStrLn versionmsg - -- \| BinaryFilename `elem` opts = putStrLn binaryfilename - | null cmd = maybe (putStr help1) (withJournalDo opts args cmd) defaultcmd - | cmd `isPrefixOf` "web" = withJournalDo opts args cmd web - -- \| cmd `isPrefixOf` "test" = runtests opts args >> return () - | otherwise = putStr help1 - - defaultcmd = Just web + run opts args + | Help `elem` opts = putStr usage_web + | Version `elem` opts = putStrLn $ progversionstr progname_web + | BinaryFilename `elem` opts = putStrLn $ binaryfilename progname_web + | otherwise = withJournalDo opts args "web" web -- | The web command. web :: [Opt] -> [String] -> Journal -> IO () diff --git a/hledger/Hledger/Cli/Convert.hs b/hledger/Hledger/Cli/Convert.hs index bb7986179..ed8be5be7 100644 --- a/hledger/Hledger/Cli/Convert.hs +++ b/hledger/Hledger/Cli/Convert.hs @@ -4,8 +4,8 @@ format, and print it on stdout. See the manual for more details. -} module Hledger.Cli.Convert where -import Hledger.Cli.Options (Opt(Debug)) -import Hledger.Cli.Version (versionstr) +import Hledger.Cli.Options (Opt(Debug), progname_cli) +import Hledger.Cli.Version (progversionstr) import Hledger.Data.Types (Journal,AccountName,Transaction(..),Posting(..),PostingType(..)) import Hledger.Data.Utils (strip, spacenonewline, restofline, parseWithCtx, assertParse, assertParseEqual, error') import Hledger.Read.Journal (someamount,ledgeraccountname) @@ -116,7 +116,7 @@ rulesFileFor csvfile = replaceExtension csvfile ".rules" initialRulesFileContent :: String initialRulesFileContent = - "# csv conversion rules file generated by hledger "++versionstr++"\n" ++ + "# csv conversion rules file generated by "++(progversionstr progname_cli)++"\n" ++ "# Add rules to this file for more accurate conversion, see\n"++ "# http://hledger.org/MANUAL.html#convert\n" ++ "\n" ++ diff --git a/hledger/Hledger/Cli/Main.hs b/hledger/Hledger/Cli/Main.hs index 40603eb1d..c44e160ea 100644 --- a/hledger/Hledger/Cli/Main.hs +++ b/hledger/Hledger/Cli/Main.hs @@ -50,20 +50,18 @@ import Hledger.Cli.Commands import Hledger.Cli.Options import Hledger.Cli.Tests import Hledger.Cli.Utils (withJournalDo) -import Hledger.Cli.Version (versionmsg, binaryfilename) +import Hledger.Cli.Version (progversionstr, binaryfilename) main :: IO () main = do - (opts, cmd, args) <- parseArguments + (opts, cmd, args) <- parseArgumentsWith options_cli usage_cli run cmd opts args where run cmd opts args - | Help `elem` opts = putStr help1 - | HelpOptions `elem` opts = putStr help2 - | HelpAll `elem` opts = putStr $ help1 ++ "\n" ++ help2 - | Version `elem` opts = putStrLn versionmsg - | BinaryFilename `elem` opts = putStrLn binaryfilename - | null cmd = maybe (putStr help1) (withJournalDo opts args cmd) defaultcmd + | Help `elem` opts = putStr usage_cli + | Version `elem` opts = putStrLn $ progversionstr progname_cli + | BinaryFilename `elem` opts = putStrLn $ binaryfilename progname_cli + | null cmd = maybe (putStr usage_cli) (withJournalDo opts args cmd) defaultcmd | cmd `isPrefixOf` "balance" = withJournalDo opts args cmd balance | cmd `isPrefixOf` "convert" = withJournalDo opts args cmd convert | cmd `isPrefixOf` "print" = withJournalDo opts args cmd print' @@ -72,6 +70,6 @@ main = do | cmd `isPrefixOf` "add" = withJournalDo opts args cmd add | cmd `isPrefixOf` "stats" = withJournalDo opts args cmd stats | cmd `isPrefixOf` "test" = runtests opts args >> return () - | otherwise = putStr help1 + | otherwise = putStr usage_cli defaultcmd = Nothing diff --git a/hledger/Hledger/Cli/Options.hs b/hledger/Hledger/Cli/Options.hs index a096e8b05..a6bf22e68 100644 --- a/hledger/Hledger/Cli/Options.hs +++ b/hledger/Hledger/Cli/Options.hs @@ -5,49 +5,58 @@ Command-line options for the application. module Hledger.Cli.Options where -import Safe (headDef) +import Codec.Binary.UTF8.String (decodeString) import System.Console.GetOpt import System.Environment -import Hledger.Cli.Version (timeprogname) -import Hledger.Read (myJournalPath, myTimelogPath) + import Hledger.Data.Utils import Hledger.Data.Types import Hledger.Data.Dates -import Codec.Binary.UTF8.String (decodeString) +import Hledger.Read (myJournalPath, myTimelogPath) -help1 = + +progname_cli = "hledger" + +-- | The program name which, if we are invoked as (via symlink or +-- renaming), causes us to default to reading the user's time log instead +-- of their journal. +progname_cli_time = "hours" + +usage_preamble_cli = "Usage: hledger [OPTIONS] COMMAND [PATTERNS]\n" ++ " hledger [OPTIONS] convert CSVFILE\n" ++ - " hledger [OPTIONS] stats\n" ++ "\n" ++ - "hledger reads your ~/.journal file, or another specified with $LEDGER or -f\n" ++ + "Reads your ~/.journal file, or another specified by $LEDGER or -f, and\n" ++ + "runs the specified command (may be abbreviated):\n" ++ "\n" ++ - "COMMAND is one of (may be abbreviated):\n" ++ " add - prompt for new transactions and add them to the journal\n" ++ " balance - show accounts, with balances\n" ++ - " convert - read CSV bank data and display in journal format\n" ++ + " convert - show the specified CSV file as a hledger journal\n" ++ " histogram - show a barchart of transactions per day or other interval\n" ++ " print - show transactions in journal format\n" ++ " register - show transactions as a register with running balance\n" ++ " stats - show various statistics for a journal\n" ++ " test - run self-tests\n" ++ - "\n" ++ - "PATTERNS are regular expressions which filter by account name.\n" ++ - "Prefix with desc: to filter by transaction description instead.\n" ++ - "Prefix with not: to negate a pattern. When using both, not: comes last.\n" ++ - "\n" ++ - "DATES can be y/m/d or ledger-style smart dates like \"last month\".\n" ++ - "\n" ++ - "Use --help-options to see OPTIONS, or --help-all/-H.\n" ++ - "" + "\n" -help2 = usageInfo "Options:\n" options' - where options' = filter (\(Option _ name _ _) -> not $ (headDef "" name) `elem` hiddenoptions) options - hiddenoptions = ["base-url","port","debug-vty","output","items","size"] +usage_options_cli = usageInfo "hledger options:" options_cli + +usage_postscript_cli = + "\n" ++ + "DATES can be y/m/d or smart dates like \"last month\". PATTERNS are regular\n" ++ + "expressions which filter by account name. Prefix a pattern with desc: to\n" ++ + "filter by transaction description instead, prefix with not: to negate it.\n" ++ + "When using both, not: comes last.\n" + +usage_cli = concat [ + usage_preamble_cli + ,usage_options_cli + ,usage_postscript_cli + ] -- | Command-line options we accept. -options :: [OptDescr Opt] -options = [ +options_cli :: [OptDescr Opt] +options_cli = [ Option "f" ["file"] (ReqArg File "FILE") "use a different journal/timelog file; - means stdin" ,Option "" ["no-new-accounts"] (NoArg NoNewAccts) "don't allow to create new accounts" ,Option "b" ["begin"] (ReqArg Begin "DATE") "report on transactions on or after this date" @@ -75,60 +84,45 @@ options = [ ,Option "" ["debug"] (NoArg Debug) "show extra debug output; implies verbose" ,Option "" ["binary-filename"] (NoArg BinaryFilename) "show the download filename for this hledger build" ,Option "V" ["version"] (NoArg Version) "show version information" - ,Option "h" ["help"] (NoArg Help) "show basic command-line usage" - ,Option "" ["help-options"] (NoArg HelpOptions) "show command-line options" - ,Option "H" ["help-all"] (NoArg HelpAll) "show command-line usage and options" --- hidden options needed for add-ons, for now - ,Option "" ["base-url"] (ReqArg BaseUrl "URL") "web: use this base url (default http://localhost:PORT)" - ,Option "" ["port"] (ReqArg Port "N") "web: serve on tcp port N (default 5000)" - ,Option "" ["debug-vty"] (NoArg DebugVty) "vty: run with no terminal output, showing console" - ,Option "o" ["output"] (ReqArg ChartOutput "FILE") ("chart: output filename (default: "++chartoutput++")") - ,Option "" ["items"] (ReqArg ChartItems "N") ("chart: number of accounts to show (default: "++show chartitems++")") - ,Option "" ["size"] (ReqArg ChartSize "WIDTHxHEIGHT") ("chart: image size (default: "++chartsize++")") + ,Option "h" ["help"] (NoArg Help) "show command-line usage" ] - -- - " vty - run a simple curses-style UI" ++ - -- - " web - run a simple web-based UI" ++ - -- - " chart - generate balances pie charts" ++ -chartoutput = "hledger.png" -chartitems = 10 -chartsize = "600x400" - -- | An option value from a command-line flag. data Opt = - File {value::String} | - NoNewAccts | - Begin {value::String} | - End {value::String} | - Period {value::String} | - Cleared | - UnCleared | - CostBasis | - Depth {value::String} | - Display {value::String} | - Effective | - Empty | - Real | - Flat | - Drop {value::String} | - NoTotal | - SubTotal | - DailyOpt | - WeeklyOpt | - MonthlyOpt | - QuarterlyOpt | - YearlyOpt | - Help | - HelpOptions | - HelpAll | - Verbose | - Version + File {value::String} + | NoNewAccts + | Begin {value::String} + | End {value::String} + | Period {value::String} + | Cleared + | UnCleared + | CostBasis + | Depth {value::String} + | Display {value::String} + | Effective + | Empty + | Real + | Flat + | Drop {value::String} + | NoTotal + | SubTotal + | DailyOpt + | WeeklyOpt + | MonthlyOpt + | QuarterlyOpt + | YearlyOpt + | Help + | Verbose + | Version | BinaryFilename | Debug - -- + -- XXX add-on options, must be defined here for now + -- vty | DebugVty - | BaseUrl {value::String} - | Port {value::String} + -- web + | BaseUrl {value::String} + | Port {value::String} + -- chart | ChartOutput {value::String} | ChartItems {value::String} | ChartSize {value::String} @@ -147,11 +141,14 @@ optValuesForConstructor f opts = concatMap get opts optValuesForConstructors fs opts = concatMap get opts where get o = [v | any (\f -> f v == o) fs] where v = value o --- | Parse the command-line arguments into options, command name, and --- command arguments. Any dates in the options are converted to explicit --- YYYY/MM/DD format based on the current time. -parseArguments :: IO ([Opt], String, [String]) -parseArguments = do +-- | Parse the command-line arguments into options, command name (first +-- argument), and command arguments (rest of arguments), using the +-- specified options. Any smart dates in the options are converted to +-- explicit YYYY/MM/DD format based on the current time. If parsing fails, +-- raise an error, displaying the problem along with the specified usage +-- string. +parseArgumentsWith :: [OptDescr Opt] -> String -> IO ([Opt], String, [String]) +parseArgumentsWith options usage = do args <- liftM (map decodeString) getArgs let (os,as,es) = getOpt Permute options args os' <- fixOptDates os @@ -159,7 +156,7 @@ parseArguments = do case (as,es) of (cmd:args,[]) -> return (os'',cmd,args) ([],[]) -> return (os'',"",[]) - (_,errs) -> ioError (userError' (concat errs ++ help1)) + (_,errs) -> ioError (userError' (concat errs ++ usage)) -- | Convert any fuzzy dates within these option values to explicit ones, -- based on today's date. @@ -257,7 +254,7 @@ clearedValueFromOpts opts | null os = Nothing usingTimeProgramName :: IO Bool usingTimeProgramName = do progname <- getProgName - return $ map toLower progname == timeprogname + return $ map toLower progname == progname_cli_time -- | Get the journal file path from options, an environment variable, or a default journalFilePathFromOpts :: [Opt] -> IO String diff --git a/hledger/Hledger/Cli/Version.hs b/hledger/Hledger/Cli/Version.hs index 50da3fd44..add1eca1e 100644 --- a/hledger/Hledger/Cli/Version.hs +++ b/hledger/Hledger/Cli/Version.hs @@ -4,12 +4,19 @@ Version-related utilities. See the Makefile for details of our version numbering policy. -} -module Hledger.Cli.Version +module Hledger.Cli.Version ( + version + ,progversionstr + ,binaryfilename +) where import System.Info (os, arch) + import Hledger.Data.Utils --- version and PATCHLEVEL are set by the makefile + +-- version and PATCHLEVEL are set by the make process + version = "0.13.0" #ifdef PATCHLEVEL @@ -18,15 +25,41 @@ patchlevel = "." ++ show PATCHLEVEL -- must be numeric ! patchlevel = "" #endif -progname = "hledger" -timeprogname = "hours" - buildversion = version ++ patchlevel :: String -binaryfilename = prettify $ splitAtElement '.' buildversion :: String +-- | Given a program name, return a human-readable version string. For +-- development builds, at least non-cabal builds, the patch level (ie the +-- number of patches applied since last release tag) will also be +-- included. +progversionstr :: String -> String +progversionstr progname = progname ++ "-" ++ versionstr ++ configmsg + where + versionstr = prettify $ splitAtElement '.' buildversion + where + prettify (major:minor:bugfix:patches:[]) = + printf "%s.%s%s%s" major minor bugfix' patches' + where + bugfix' + | bugfix `elem` ["0"{-,"98","99"-}] = "" + | otherwise = '.' : bugfix + patches' + | patches/="0" = "+"++patches + | otherwise = "" + prettify s = intercalate "." s + + configmsg | null buildflags = "" + | otherwise = " with " ++ intercalate ", " buildflags + + buildflags = [] + +-- | Given a program name, return a precise platform-specific executable +-- name suitable for naming downloadable binaries. Can raise an error if +-- the version and patch level was not defined correctly at build time. +binaryfilename :: String -> String +binaryfilename progname = prettify $ splitAtElement '.' buildversion where prettify (major:minor:bugfix:patches:[]) = - printf "hledger-%s.%s%s%s-%s-%s%s" major minor bugfix' patches' os' arch suffix + printf "%s-%s.%s%s%s-%s-%s%s" progname major minor bugfix' patches' os' arch suffix where bugfix' | bugfix `elem` ["0"{-,"98","99"-}] = "" @@ -43,24 +76,3 @@ binaryfilename = prettify $ splitAtElement '.' buildversion :: String prettify (major:[]) = prettify [major,"0","0","0"] prettify [] = error' "VERSION is empty, please fix" prettify _ = error' "VERSION has too many components, please fix" - -versionstr = prettify $ splitAtElement '.' buildversion :: String - where - prettify (major:minor:bugfix:patches:[]) = - printf "%s.%s%s%s" major minor bugfix' patches' - where - bugfix' - | bugfix `elem` ["0"{-,"98","99"-}] = "" - | otherwise = '.' : bugfix - patches' - | patches/="0" = "+"++patches - | otherwise = "" - prettify s = intercalate "." s - -versionmsg = progname ++ "-" ++ versionstr ++ configmsg :: String - where configmsg - | null configflags = " with no extras" - | otherwise = " with " ++ intercalate ", " configflags - -configflags = tail ["" - ]