diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,18 @@
 Most user-visible changes are noted in the hledger changelog, instead.
 
 
+# 1.9.1 (2018/4/30)
+
+* new generic PeriodicReport, and some report-related type aliases
+
+* new BudgetReport
+
+* make (readJournal|tryReader)s?WithOpts the default api, dropping "WithOpts"
+
+* automated postings and command line account aliases happen earlier
+  in journal processing (see hledger changelog)
+
+
 # 1.9 (2018/3/31)
 
 * support ghc 8.4, latest deps
diff --git a/Hledger/Data/AccountName.hs b/Hledger/Data/AccountName.hs
--- a/Hledger/Data/AccountName.hs
+++ b/Hledger/Data/AccountName.hs
@@ -57,8 +57,24 @@
 accountNameLevel "" = 0
 accountNameLevel a = T.length (T.filter (==acctsepchar) a) + 1
 
+-- | A top-level account prefixed to some accounts in budget reports.
+-- Defined here so it can be ignored by accountNameDrop. 
+unbudgetedAccountName :: T.Text
+unbudgetedAccountName = "<unbudgeted>"
+
+-- | Remove some number of account name components from the front of the account name.
+-- If the special "<unbudgeted>" top-level account is present, it is preserved and
+-- dropping affects the rest of the account name. 
 accountNameDrop :: Int -> AccountName -> AccountName
-accountNameDrop n = accountNameFromComponents . drop n . accountNameComponents
+accountNameDrop n a
+  | a == unbudgetedAccountName = a
+  | unbudgetedAccountAndSep `T.isPrefixOf` a =
+      case accountNameDrop n $ T.drop (T.length unbudgetedAccountAndSep) a of
+        "" -> unbudgetedAccountName
+        a' -> unbudgetedAccountAndSep <> a'
+  | otherwise = accountNameFromComponents $ drop n $ accountNameComponents a
+  where 
+    unbudgetedAccountAndSep = unbudgetedAccountName <> acctsep
 
 -- | Sorted unique account names implied by these account names,
 -- ie these plus all their parent accounts up to the root.
diff --git a/Hledger/Data/Amount.hs b/Hledger/Data/Amount.hs
--- a/Hledger/Data/Amount.hs
+++ b/Hledger/Data/Amount.hs
@@ -61,6 +61,7 @@
   amountValue,
   -- ** rendering
   amountstyle,
+  styleAmount,
   showAmount,
   cshowAmount,
   showAmountWithZeroCommodity,
@@ -93,6 +94,7 @@
   isReallyZeroMixedAmountCost,
   mixedAmountValue,
   -- ** rendering
+  styleMixedAmount,
   showMixedAmount,
   showMixedAmountOneLine,
   showMixedAmountDebug,
@@ -131,8 +133,14 @@
 
 deriving instance Show MarketPrice
 
+
+-------------------------------------------------------------------------------
+-- Amount styles
+
+-- | Default amount style 
 amountstyle = AmountStyle L False 0 (Just '.') Nothing
 
+
 -------------------------------------------------------------------------------
 -- Amount
 
@@ -265,6 +273,14 @@
 showPriceDebug (UnitPrice pa)  = " @ "  ++ showAmountDebug pa
 showPriceDebug (TotalPrice pa) = " @@ " ++ showAmountDebug pa
 
+-- | Given a map of standard amount display styles, apply the appropriate one to this amount.
+-- If there's no standard style for this amount's commodity, return the amount unchanged.
+styleAmount :: M.Map CommoditySymbol AmountStyle -> Amount -> Amount
+styleAmount styles a =
+  case M.lookup (acommodity a) styles of
+    Just s  -> a{astyle=s}
+    Nothing -> a 
+
 -- | Get the string representation of an amount, based on its
 -- commodity's display settings. String representations equivalent to
 -- zero are converted to just \"0\". The special "missing" amount is
@@ -555,6 +571,10 @@
 --     where a' = normaliseMixedAmountSquashPricesForDisplay a
 --           b' = normaliseMixedAmountSquashPricesForDisplay b
 
+-- | Given a map of standard amount display styles, apply the appropriate ones to each individual amount.
+styleMixedAmount :: M.Map CommoditySymbol AmountStyle -> MixedAmount -> MixedAmount
+styleMixedAmount styles (Mixed as) = Mixed $ map (styleAmount styles) as 
+
 -- | Get the string representation of a mixed amount, after
 -- normalising it to one amount per commodity. Assumes amounts have
 -- no or similar prices, otherwise this can show misleading prices.
@@ -647,6 +667,7 @@
 
 mixedAmountValue :: Journal -> Day -> MixedAmount -> MixedAmount
 mixedAmountValue j d (Mixed as) = Mixed $ map (amountValue j d) as
+
 
 -------------------------------------------------------------------------------
 -- misc
diff --git a/Hledger/Data/AutoTransaction.hs b/Hledger/Data/AutoTransaction.hs
--- a/Hledger/Data/AutoTransaction.hs
+++ b/Hledger/Data/AutoTransaction.hs
@@ -71,8 +71,9 @@
 runModifierTransaction q mt = modifier where
     q' = simplifyQuery $ And [q, mtvaluequery mt (error "query cannot depend on current time")]
     mods = map runModifierPosting $ mtpostings mt
-    generatePostings ps = [m p | p <- ps, q' `matchesPosting` p, m <- mods]
-    modifier t@(tpostings -> ps) = t { tpostings = ps ++ generatePostings ps }
+    generatePostings ps = [p' | p <- ps
+                              , p' <- if q' `matchesPosting` p then p:[ m p | m <- mods] else [p]]
+    modifier t@(tpostings -> ps) = t { tpostings = generatePostings ps }
 
 -- | Extract 'Query' equivalent of 'mtvalueexpr' from 'ModifierTransaction'
 --
diff --git a/Hledger/Data/Journal.hs b/Hledger/Data/Journal.hs
--- a/Hledger/Data/Journal.hs
+++ b/Hledger/Data/Journal.hs
@@ -16,10 +16,10 @@
   addModifierTransaction,
   addPeriodicTransaction,
   addTransaction,
-  journalApplyAliases,
   journalBalanceTransactions,
   journalApplyCommodityStyles,
   commodityStylesFromAmounts,
+  journalCommodityStyles,
   journalConvertAmountsToCost,
   journalFinalise,
   journalPivot,
@@ -485,19 +485,6 @@
 
 -}
 
--- | Apply additional account aliases (eg from the command-line) to all postings in a journal.
-journalApplyAliases :: [AccountAlias] -> Journal -> Journal
-journalApplyAliases aliases j@Journal{jtxns=ts} =
-  -- (if null aliases
-  --  then id
-  --  else (dbgtrace $
-  --        "applying additional command-line aliases:\n"
-  --        ++ chomp (unlines $ map (" "++) $ lines $ ppShow aliases))) $
-  j{jtxns=map dotransaction ts}
-    where
-      dotransaction t@Transaction{tpostings=ps} = t{tpostings=map doposting ps}
-      doposting p@Posting{paccount=a} = p{paccount= accountNameApplyAliases aliases a}
-
 -- | Do post-parse processing on a parsed journal to make it ready for
 -- use.  Reverse parsed data to normal order, canonicalise amount
 -- formats, check/ensure that transactions are balanced, and maybe
@@ -617,8 +604,11 @@
   runExceptT $ do
     bals <- lift $ HT.newSized size
     txStore <- lift $ createStore
-    flip R.runReaderT (Env bals (storeIn txStore) assrt $
-                       Just $ jinferredcommodities j) $ do
+    let env = Env bals 
+                  (storeIn txStore) 
+                  assrt
+                  (Just $ journalCommodityStyles j)
+    flip R.runReaderT env $ do
       dated <- fmap snd . sortBy (comparing fst) . concat
              <$> mapM' discriminateByDate (jtxns j)
       mapM' checkInferAndRegisterAmounts dated
@@ -738,7 +728,6 @@
 liftModifier :: (Env s -> ST s a) -> CurrentBalancesModifier s a
 liftModifier f = R.ask >>= lift . lift . f
 
-
 -- | Choose and apply a consistent display format to the posting
 -- amounts in each commodity. Each commodity's format is specified by
 -- a commodity format directive, or otherwise inferred from posting
@@ -747,28 +736,20 @@
 journalApplyCommodityStyles j@Journal{jtxns=ts, jmarketprices=mps} = j''
     where
       j' = journalInferCommodityStyles j
+      styles = journalCommodityStyles j'
       j'' = j'{jtxns=map fixtransaction ts, jmarketprices=map fixmarketprice mps}
       fixtransaction t@Transaction{tpostings=ps} = t{tpostings=map fixposting ps}
-      fixposting p@Posting{pamount=a} = p{pamount=fixmixedamount a}
-      fixmarketprice mp@MarketPrice{mpamount=a} = mp{mpamount=fixamount a}
-      fixmixedamount (Mixed as) = Mixed $ map fixamount as
-      fixamount a@Amount{acommodity=c} = a{astyle=journalCommodityStyle j' c}
-
--- | Get this journal's standard display style for the given
--- commodity.  That is the style defined by the last corresponding
--- commodity format directive if any, otherwise the style inferred
--- from the posting amounts (or in some cases, price amounts) in this
--- commodity if any, otherwise the default style.
-journalCommodityStyle :: Journal -> CommoditySymbol -> AmountStyle
-journalCommodityStyle j = fromMaybe amountstyle{asprecision=2} . journalCommodityStyleLookup j
+      fixposting p@Posting{pamount=a} = p{pamount=styleMixedAmount styles a}
+      fixmarketprice mp@MarketPrice{mpamount=a} = mp{mpamount=styleAmount styles a}
 
-journalCommodityStyleLookup :: Journal -> CommoditySymbol -> Maybe AmountStyle
-journalCommodityStyleLookup j c =
-  listToMaybe $
-  catMaybes [
-     M.lookup c (jcommodities j) >>= cformat
-    ,M.lookup c $ jinferredcommodities j
-    ]
+-- | Get all the amount styles defined in this journal, either 
+-- declared by a commodity directive (preferred) or inferred from amounts,
+-- as a map from symbol to style. 
+journalCommodityStyles :: Journal -> M.Map CommoditySymbol AmountStyle
+journalCommodityStyles j = declaredstyles <> inferredstyles
+  where
+    declaredstyles = M.mapMaybe cformat $ jcommodities j
+    inferredstyles = jinferredcommodities j
 
 -- | Infer a display format for each commodity based on the amounts parsed.
 -- "hledger... will use the format of the first posting amount in the
@@ -776,8 +757,8 @@
 journalInferCommodityStyles :: Journal -> Journal
 journalInferCommodityStyles j =
   j{jinferredcommodities =
-        commodityStylesFromAmounts $
-        dbg8 "journalChooseCommmodityStyles using amounts" $ journalAmounts j}
+    commodityStylesFromAmounts $
+    dbg8 "journalInferCommmodityStyles using amounts" $ journalAmounts j}
 
 -- | Given a list of amounts in parse order, build a map from their commodity names
 -- to standard commodity display formats.
@@ -833,10 +814,8 @@
       fixtransaction t@Transaction{tpostings=ps} = t{tpostings=map fixposting ps}
       fixposting p@Posting{pamount=a} = p{pamount=fixmixedamount a}
       fixmixedamount (Mixed as) = Mixed $ map fixamount as
-      fixamount = applyJournalStyle . costOfAmount
-      applyJournalStyle a
-          | Just s <- journalCommodityStyleLookup j (acommodity a) = a{astyle=s}
-          | otherwise = a
+      fixamount = styleAmount styles . costOfAmount
+      styles = journalCommodityStyles j
 
 -- -- | Get this journal's unique, display-preference-canonicalised commodities, by symbol.
 -- journalCanonicalCommodities :: Journal -> M.Map String CommoditySymbol
diff --git a/Hledger/Data/Transaction.hs b/Hledger/Data/Transaction.hs
--- a/Hledger/Data/Transaction.hs
+++ b/Hledger/Data/Transaction.hs
@@ -278,7 +278,7 @@
     "inferBalancingAmount" ~: do
     let p `gives` p' = assertEqual (show p) (Right p') $ inferTransaction p
         inferTransaction :: Transaction -> Either String Transaction
-        inferTransaction = runIdentity . runExceptT . inferBalancingAmount (\_ _ -> return ())
+        inferTransaction = runIdentity . runExceptT . inferBalancingAmount (\_ _ -> return ()) Map.empty
     nulltransaction `gives` nulltransaction
     nulltransaction{
       tpostings=[
@@ -382,11 +382,11 @@
      -- ^ update function
   -> Maybe (Map.Map CommoditySymbol AmountStyle)
   -> Transaction -> m Transaction
-balanceTransactionUpdate update styles t =
-  finalize =<< inferBalancingAmount update t
+balanceTransactionUpdate update mstyles t =
+  finalize =<< inferBalancingAmount update (fromMaybe Map.empty mstyles) t
   where
     finalize t' = let t'' = inferBalancingPrices t'
-                  in if isTransactionBalanced styles t''
+                  in if isTransactionBalanced mstyles t''
                      then return $ txnTieKnot t''
                      else throwError $ printerr $ nonzerobalanceerror t''
     printerr s = intercalate "\n" [s, showTransactionUnelided t]
@@ -409,11 +409,12 @@
 -- We can infer a missing amount when there are multiple postings and exactly
 -- one of them is amountless. If the amounts had price(s) the inferred amount
 -- have the same price(s), and will be converted to the price commodity.
-inferBalancingAmount :: MonadError String m
-                     => (AccountName -> MixedAmount -> m ())
-                     -- ^ update function
-                     -> Transaction -> m Transaction
-inferBalancingAmount update t@Transaction{tpostings=ps}
+inferBalancingAmount :: MonadError String m =>
+                        (AccountName -> MixedAmount -> m ()) -- ^ update function
+                     -> Map.Map CommoditySymbol AmountStyle  -- ^ standard amount styles
+                     -> Transaction
+                     -> m Transaction
+inferBalancingAmount update styles t@Transaction{tpostings=ps}
   | length amountlessrealps > 1
       = throwError $ printerr "could not balance this transaction - can't have more than one real posting with no amount (remember to put 2 or more spaces before amounts)"
   | length amountlessbvps > 1
@@ -432,8 +433,13 @@
     inferamount p@Posting{ptype=BalancedVirtualPosting}
      | not (hasAmount p) = updateAmount p bvsum
     inferamount p = return p
-    updateAmount p amt = update (paccount p) amt' >> return p { pamount=amt', porigin=Just $ originalPosting p }
-      where amt' = normaliseMixedAmount $ costOfMixedAmount (-amt)
+    updateAmount p amt = 
+      update (paccount p) amt' >> return p { pamount=amt', porigin=Just $ originalPosting p }
+      where
+        -- Inferred amounts are converted to cost.
+        -- Also, ensure the new amount has the standard style for its commodity  
+        -- (the main amount styling pass happened before this balancing pass).   
+        amt' = styleMixedAmount styles $ normaliseMixedAmount $ costOfMixedAmount (-amt)
 
 -- | Infer prices for this transaction's posting amounts, if needed to make
 -- the postings balance, and if possible. This is done once for the real
diff --git a/Hledger/Data/Types.hs b/Hledger/Data/Types.hs
--- a/Hledger/Data/Types.hs
+++ b/Hledger/Data/Types.hs
@@ -22,7 +22,6 @@
 
 import GHC.Generics (Generic)
 import Control.DeepSeq (NFData)
-import Control.Monad.Except (ExceptT)
 import Data.Data
 import Data.Decimal
 import Data.Default
@@ -303,7 +302,7 @@
   -- principal data
   ,jaccounts              :: [(AccountName, Maybe AccountCode)]     -- ^ accounts that have been declared by account directives
   ,jcommodities           :: M.Map CommoditySymbol Commodity        -- ^ commodities and formats declared by commodity directives
-  ,jinferredcommodities   :: M.Map CommoditySymbol AmountStyle      -- ^ commodities and formats inferred from journal amounts
+  ,jinferredcommodities   :: M.Map CommoditySymbol AmountStyle      -- ^ commodities and formats inferred from journal amounts  XXX misnamed
   ,jmarketprices          :: [MarketPrice]
   ,jmodifiertxns          :: [ModifierTransaction]
   ,jperiodictxns          :: [PeriodicTransaction]
@@ -328,28 +327,6 @@
 -- | The id of a data format understood by hledger, eg @journal@ or @csv@.
 -- The --output-format option selects one of these for output.
 type StorageFormat = String
-
--- | A hledger journal reader is a triple of storage format name, a
--- detector of that format, and a parser from that format to Journal.
-data Reader = Reader {
-
-     -- The canonical name of the format handled by this reader
-     rFormat   :: StorageFormat
-
-     -- The file extensions recognised as containing this format
-    ,rExtensions :: [String]
-
-     -- A text parser for this format, accepting an optional rules file,
-     -- assertion-checking flag, and file path for error messages,
-     -- producing an exception-raising IO action that returns a journal
-     -- or error message.
-    ,rParser   :: Maybe FilePath -> Bool -> FilePath -> Text -> ExceptT String IO Journal
-
-     -- Experimental readers are never tried automatically.
-    ,rExperimental :: Bool
-    }
-
-instance Show Reader where show r = rFormat r ++ " reader"
 
 -- | An account, with name, balances and links to parent/subaccounts
 -- which let you walk up or down the account tree.
diff --git a/Hledger/Read.hs b/Hledger/Read.hs
--- a/Hledger/Read.hs
+++ b/Hledger/Read.hs
@@ -14,7 +14,6 @@
   PrefixedFilePath,
   defaultJournal,
   defaultJournalPath,
-  readJournalFilesWithOpts,
   readJournalFiles,
   readJournalFile,
   requireJournalFileExists,
@@ -26,7 +25,6 @@
   readJournal',
 
   -- * Re-exported
-  JournalReader.accountaliasp,
   JournalReader.postingp,
   module Hledger.Read.Common,
 
@@ -36,10 +34,10 @@
 
 ) where
 
-import Control.Applicative ((<|>))
 import Control.Arrow (right)
 import qualified Control.Exception as C
 import Control.Monad.Except
+import Data.Default
 import Data.List
 import Data.Maybe
 import Data.Ord
@@ -90,7 +88,7 @@
 
 -- | Read the default journal file specified by the environment, or raise an error.
 defaultJournal :: IO Journal
-defaultJournal = defaultJournalPath >>= readJournalFile Nothing Nothing True >>= either error' return
+defaultJournal = defaultJournalPath >>= readJournalFile def >>= either error' return
 
 -- | Get the default journal file path specified by the environment.
 -- Like ledger, we look first for the LEDGER_FILE environment
@@ -112,52 +110,6 @@
                   home <- getHomeDirectory `C.catch` (\(_::C.IOException) -> return "")
                   return $ home </> journalDefaultFilename
 
--- | @readJournalFiles mformat mrulesfile assrt prefixedfiles@
---
--- Read a Journal from each specified file path and combine them into one.
--- Or, return the first error message.
---
--- Combining Journals means concatenating them, basically.
--- The parse state resets at the start of each file, which means that
--- directives & aliases do not cross file boundaries.
--- (The final parse state saved in the Journal does span all files, however.)
---
--- As with readJournalFile,
--- file paths can optionally have a READER: prefix,
--- and the @mformat@, @mrulesfile, and @assrt@ arguments are supported
--- (and these are applied to all files).
---
-readJournalFiles :: Maybe StorageFormat -> Maybe FilePath -> Bool -> [PrefixedFilePath] -> IO (Either String Journal)
-readJournalFiles mformat mrulesfile assrt prefixedfiles = do
-  (right mconcat1 . sequence)
-    <$> mapM (readJournalFile mformat mrulesfile assrt) prefixedfiles
-  where mconcat1 :: Monoid t => [t] -> t
-        mconcat1 [] = mempty
-        mconcat1 x = foldr1 mappend x
-
--- | @readJournalFile mformat mrulesfile assrt prefixedfile@
---
--- Read a Journal from this file, or from stdin if the file path is -,
--- or return an error message. The file path can have a READER: prefix.
---
--- The reader (data format) is chosen based on (in priority order):
--- the @mformat@ argument;
--- the file path's READER: prefix, if any;
--- a recognised file name extension (in readJournal);
--- if none of these identify a known reader, all built-in readers are tried in turn.
---
--- A CSV conversion rules file (@mrulesfiles@) can be specified to help convert CSV data.
---
--- Optionally, any balance assertions in the journal can be checked (@assrt@).
---
-readJournalFile :: Maybe StorageFormat -> Maybe FilePath -> Bool -> PrefixedFilePath -> IO (Either String Journal)
-readJournalFile mformat mrulesfile assrt prefixedfile = do
-  let
-    (mprefixformat, f) = splitReaderPrefix prefixedfile
-    mfmt = mformat <|> mprefixformat
-  requireJournalFileExists f
-  readFileOrStdinPortably f >>= readJournal mfmt mrulesfile assrt (Just f)
-
 -- | If a filepath is prefixed by one of the reader names and a colon,
 -- split that off. Eg "csv:-" -> (Just "csv", "-").
 splitReaderPrefix :: PrefixedFilePath -> (Maybe String, FilePath)
@@ -195,7 +147,7 @@
 
 -- | Read a Journal from the given text trying all readers in turn, or throw an error.
 readJournal' :: Text -> IO Journal
-readJournal' t = readJournal Nothing Nothing True Nothing t >>= either error' return
+readJournal' t = readJournal def Nothing t >>= either error' return
 
 tests_readJournal' = [
   "readJournal' parses sample journal" ~: do
@@ -203,28 +155,6 @@
      assertBool "" True
  ]
 
--- | @readJournal mformat mrulesfile assrt mfile txt@
---
--- Read a Journal from some text, or return an error message.
---
--- The reader (data format) is chosen based on (in priority order):
--- the @mformat@ argument;
--- a recognised file name extension in @mfile@ (if provided).
--- If none of these identify a known reader, all built-in readers are tried in turn
--- (returning the first one's error message if none of them succeed).
---
--- A CSV conversion rules file (@mrulesfiles@) can be specified to help convert CSV data.
---
--- Optionally, any balance assertions in the journal can be checked (@assrt@).
---
-readJournal :: Maybe StorageFormat -> Maybe FilePath -> Bool -> Maybe FilePath -> Text -> IO (Either String Journal)
-readJournal mformat mrulesfile assrt mfile txt =
-  let
-    stablereaders = filter (not.rExperimental) readers
-    rs = maybe stablereaders (:[]) $ findReader mformat mfile
-  in
-    tryReaders rs mrulesfile assrt mfile txt
-
 -- | @findReader mformat mpath@
 --
 -- Find the reader named by @mformat@, if provided.
@@ -241,43 +171,41 @@
     (prefix,path') = splitReaderPrefix path
     ext            = drop 1 $ takeExtension path'
 
--- | @tryReaders readers mrulesfile assrt path t@
+-- | Read a Journal from each specified file path and combine them into one.
+-- Or, return the first error message.
 --
--- Try to parse the given text to a Journal using each reader in turn,
--- returning the first success, or if all of them fail, the first error message.
-tryReaders :: [Reader] -> Maybe FilePath -> Bool -> Maybe FilePath -> Text -> IO (Either String Journal)
-tryReaders readers mrulesfile assrt path t = firstSuccessOrFirstError [] readers
-  where
-    firstSuccessOrFirstError :: [String] -> [Reader] -> IO (Either String Journal)
-    firstSuccessOrFirstError [] []        = return $ Left "no readers found"
-    firstSuccessOrFirstError errs (r:rs) = do
-      dbg1IO "trying reader" (rFormat r)
-      result <- (runExceptT . (rParser r) mrulesfile assrt path') t
-      dbg1IO "reader result" $ either id show result
-      case result of Right j -> return $ Right j                        -- success!
-                     Left e  -> firstSuccessOrFirstError (errs++[e]) rs -- keep trying
-    firstSuccessOrFirstError (e:_) []    = return $ Left e              -- none left, return first error
-    path' = fromMaybe "(string)" path
-
-
---- New versions of readJournal* with easier arguments, and support for --new.
-
-readJournalFilesWithOpts :: InputOpts -> [FilePath] -> IO (Either String Journal)
-readJournalFilesWithOpts iopts =
-  (right mconcat1 . sequence <$>) . mapM (readJournalFileWithOpts iopts)
+-- Combining Journals means concatenating them, basically.
+-- The parse state resets at the start of each file, which means that
+-- directives & aliases do not affect subsequent sibling or parent files.
+-- They do affect included child files though. 
+-- Also the final parse state saved in the Journal does span all files.
+readJournalFiles :: InputOpts -> [FilePath] -> IO (Either String Journal)
+readJournalFiles iopts =
+  (right mconcat1 . sequence <$>) . mapM (readJournalFile iopts)
   where
     mconcat1 :: Monoid t => [t] -> t
     mconcat1 [] = mempty
     mconcat1 x  = foldr1 mappend x
 
-readJournalFileWithOpts :: InputOpts -> PrefixedFilePath -> IO (Either String Journal)
-readJournalFileWithOpts iopts prefixedfile = do
+-- | Read a Journal from this file, or from stdin if the file path is -,
+-- or return an error message. The file path can have a READER: prefix.
+--
+-- The reader (data format) to use is determined from (in priority order):
+-- the @mformat_@ specified in the input options, if any;
+-- the file path's READER: prefix, if any;
+-- a recognised file name extension.
+-- if none of these identify a known reader, all built-in readers are tried in turn.
+--
+-- The input options can also configure balance assertion checking, automated posting
+-- generation, a rules file for converting CSV data, etc.
+readJournalFile :: InputOpts -> PrefixedFilePath -> IO (Either String Journal)
+readJournalFile iopts prefixedfile = do
   let 
     (mfmt, f) = splitReaderPrefix prefixedfile
     iopts' = iopts{mformat_=firstJust [mfmt, mformat_ iopts]}
   requireJournalFileExists f
   t <- readFileOrStdinPortably f
-  ej <- readJournalWithOpts iopts' (Just f) t
+  ej <- readJournal iopts' (Just f) t
   case ej of
     Left e  -> return $ Left e
     Right j | new_ iopts -> do
@@ -342,21 +270,40 @@
     j'                    = j{jtxns=newsamedatets++laterts}
     ds'                   = latestDates $ map tdate $ samedatets++laterts
 
-readJournalWithOpts :: InputOpts -> Maybe FilePath -> Text -> IO (Either String Journal)
-readJournalWithOpts iopts mfile txt =
-  tryReadersWithOpts iopts mfile specifiedorallreaders txt
+-- | @readJournal iopts mfile txt@
+--
+-- Read a Journal from some text, or return an error message.
+--
+-- The reader (data format) is chosen based on a recognised file name extension in @mfile@ (if provided).
+-- If it does not identify a known reader, all built-in readers are tried in turn
+-- (returning the first one's error message if none of them succeed).
+--
+-- Input ioptions (@iopts@) specify CSV conversion rules file to help convert CSV data,
+-- enable or disable balance assertion checking and automated posting generation.
+--
+readJournal :: InputOpts -> Maybe FilePath -> Text -> IO (Either String Journal)
+readJournal iopts mfile txt =
+  tryReaders iopts mfile specifiedorallreaders txt
   where
     specifiedorallreaders = maybe stablereaders (:[]) $ findReader (mformat_ iopts) mfile
     stablereaders = filter (not.rExperimental) readers
 
-tryReadersWithOpts :: InputOpts -> Maybe FilePath -> [Reader] -> Text -> IO (Either String Journal)
-tryReadersWithOpts iopts mpath readers txt = firstSuccessOrFirstError [] readers
+-- | @tryReaders iopts readers path t@
+--
+-- Try to parse the given text to a Journal using each reader in turn,
+-- returning the first success, or if all of them fail, the first error message.
+--    
+-- Input ioptions (@iopts@) specify CSV conversion rules file to help convert CSV data,
+-- enable or disable balance assertion checking and automated posting generation.
+--
+tryReaders :: InputOpts -> Maybe FilePath -> [Reader] -> Text -> IO (Either String Journal)
+tryReaders iopts mpath readers txt = firstSuccessOrFirstError [] readers
   where
     firstSuccessOrFirstError :: [String] -> [Reader] -> IO (Either String Journal)
     firstSuccessOrFirstError [] []        = return $ Left "no readers found"
     firstSuccessOrFirstError errs (r:rs) = do
       dbg1IO "trying reader" (rFormat r)
-      result <- (runExceptT . (rParser r) (mrules_file_ iopts) (not $ ignore_assertions_ iopts) path) txt
+      result <- (runExceptT . (rParser r) iopts path) txt
       dbg1IO "reader result" $ either id show result
       case result of Right j -> return $ Right j                        -- success!
                      Left e  -> firstSuccessOrFirstError (errs++[e]) rs -- keep trying
@@ -408,7 +355,7 @@
    "journal" ~: do
     r <- runExceptT $ parseWithState mempty JournalReader.journalp ""
     assertBool "journalp should parse an empty file" (isRight $ r)
-    jE <- readJournal Nothing Nothing True Nothing "" -- don't know how to get it from journal
+    jE <- readJournal def Nothing "" -- don't know how to get it from journal
     either error' (assertBool "journalp parsing an empty file should give an empty journal" . null . jtxns) jE
 
   ]
diff --git a/Hledger/Read/Common.hs b/Hledger/Read/Common.hs
--- a/Hledger/Read/Common.hs
+++ b/Hledger/Read/Common.hs
@@ -47,7 +47,29 @@
 
 import Hledger.Data
 import Hledger.Utils
+import qualified Hledger.Query as Q (Query(Any))
 
+-- | A hledger journal reader is a triple of storage format name, a
+-- detector of that format, and a parser from that format to Journal.
+data Reader = Reader {
+
+     -- The canonical name of the format handled by this reader
+     rFormat   :: StorageFormat
+
+     -- The file extensions recognised as containing this format
+    ,rExtensions :: [String]
+
+     -- A text parser for this format, accepting input options, file
+     -- path for error messages and file contents, producing an exception-raising IO
+     -- action that returns a journal or error message.
+    ,rParser   :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
+
+     -- Experimental readers are never tried automatically.
+    ,rExperimental :: Bool
+    }
+
+instance Show Reader where show r = rFormat r ++ " reader"
+
 -- $setup
 
 -- | Various options to use when reading journal files.
@@ -63,12 +85,13 @@
     ,new_               :: Bool                 -- ^ read only new transactions since this file was last read
     ,new_save_          :: Bool                 -- ^ save latest new transactions state for next time
     ,pivot_             :: String               -- ^ use the given field's value as the account name 
+    ,auto_              :: Bool                 -- ^ generate automatic postings when journal is parsed     
  } deriving (Show, Data) --, Typeable)
 
 instance Default InputOpts where def = definputopts
 
 definputopts :: InputOpts
-definputopts = InputOpts def def def def def def True def
+definputopts = InputOpts def def def def def def True def def
 
 rawOptsToInputOpts :: RawOpts -> InputOpts
 rawOptsToInputOpts rawopts = InputOpts{
@@ -81,6 +104,7 @@
   ,new_               = boolopt "new" rawopts
   ,new_save_          = True
   ,pivot_             = stringopt "pivot" rawopts
+  ,auto_              = boolopt "auto" rawopts                        
   }
 
 --- * parsing utils
@@ -115,27 +139,40 @@
             | otherwise = unPos $ sourceLine p' -- might be at end of file withat last new-line
 
 
--- | Given a megaparsec ParsedJournal parser, balance assertion flag, file
+-- | Generate Automatic postings and add them to the current journal.
+generateAutomaticPostings :: Journal -> Journal
+generateAutomaticPostings j = j { jtxns = map modifier $ jtxns j }
+  where
+    modifier = foldr (flip (.) . runModifierTransaction') id mtxns
+    runModifierTransaction' = fmap txnTieKnot . runModifierTransaction Q.Any
+    mtxns = jmodifiertxns j
+
+-- | Given a megaparsec ParsedJournal parser, input options, file
 -- path and file content: parse and post-process a Journal, or give an error.
-parseAndFinaliseJournal :: ErroringJournalParser IO ParsedJournal -> Bool
-                        -> FilePath -> Text -> ExceptT String IO Journal
-parseAndFinaliseJournal parser assrt f txt = do
+parseAndFinaliseJournal :: ErroringJournalParser IO ParsedJournal -> InputOpts
+                           -> FilePath -> Text -> ExceptT String IO Journal
+parseAndFinaliseJournal parser iopts f txt = do
   t <- liftIO getClockTime
   y <- liftIO getCurrentYear
   ep <- runParserT (evalStateT parser nulljournal {jparsedefaultyear=Just y}) f txt
   case ep of
-    Right pj -> case journalFinalise t f txt assrt pj of
+    Right pj -> 
+      let pj' = if auto_ iopts then generateAutomaticPostings pj else pj in
+      case journalFinalise t f txt (not $ ignore_assertions_ iopts) pj' of
                         Right j -> return j
                         Left e  -> throwError e
     Left e   -> throwError $ parseErrorPretty e
 
-parseAndFinaliseJournal' :: JournalParser Identity ParsedJournal -> Bool -> FilePath -> Text -> ExceptT String IO Journal
-parseAndFinaliseJournal' parser assrt f txt = do
+parseAndFinaliseJournal' :: JournalParser Identity ParsedJournal -> InputOpts 
+                            -> FilePath -> Text -> ExceptT String IO Journal
+parseAndFinaliseJournal' parser iopts f txt = do
   t <- liftIO getClockTime
   y <- liftIO getCurrentYear
   let ep = runParser (evalStateT parser nulljournal {jparsedefaultyear=Just y}) f txt
   case ep of
-    Right pj -> case journalFinalise t f txt assrt pj of
+    Right pj -> 
+      let pj' = if auto_ iopts then generateAutomaticPostings pj else pj in      
+      case journalFinalise t f txt (not $ ignore_assertions_ iopts) pj' of
                         Right j -> return j
                         Left e  -> throwError e
     Left e   -> throwError $ parseErrorPretty e
diff --git a/Hledger/Read/CsvReader.hs b/Hledger/Read/CsvReader.hs
--- a/Hledger/Read/CsvReader.hs
+++ b/Hledger/Read/CsvReader.hs
@@ -60,7 +60,7 @@
 import Hledger.Data
 import Hledger.Utils.UTF8IOCompat (getContents)
 import Hledger.Utils
-import Hledger.Read.Common (amountp, statusp, genericSourcePos)
+import Hledger.Read.Common (Reader(..),InputOpts(..),amountp, statusp, genericSourcePos)
 
 
 reader :: Reader
@@ -73,8 +73,9 @@
 
 -- | Parse and post-process a "Journal" from CSV data, or give an error.
 -- XXX currently ignores the string and reads from the file path
-parse :: Maybe FilePath -> Bool -> FilePath -> Text -> ExceptT String IO Journal
-parse rulesfile _ f t = do
+parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
+parse iopts f t = do
+  let rulesfile = mrules_file_ iopts
   r <- liftIO $ readJournalFromCsv rulesfile f t
   case r of Left e -> throwError e
             Right j -> return $ journalNumberAndTieTransactions j
@@ -725,10 +726,26 @@
 type CsvAmountString = String
 
 -- | Canonicalise the sign in a CSV amount string.
--- Such strings can be parenthesized, which is equivalent to having a minus sign.
--- Also they can end up with a double minus sign, which cancels out.
+-- Such strings can have a minus sign, negating parentheses, 
+-- or any two of these (which cancels out).
+--
+-- >>> simplifySign "1"
+-- "1"
+-- >>> simplifySign "-1"
+-- "-1"
+-- >>> simplifySign "(1)"
+-- "-1"
+-- >>> simplifySign "--1"
+-- "1"
+-- >>> simplifySign "-(1)"
+-- "1"
+-- >>> simplifySign "(-1)"
+-- "1"
+-- >>> simplifySign "((1))"
+-- "1"
 simplifySign :: CsvAmountString -> CsvAmountString
 simplifySign ('(':s) | lastMay s == Just ')' = simplifySign $ negateStr $ init s
+simplifySign ('-':'(':s) | lastMay s == Just ')' = simplifySign $ init s
 simplifySign ('-':'-':s) = s
 simplifySign s = s
 
diff --git a/Hledger/Read/JournalReader.hs b/Hledger/Read/JournalReader.hs
--- a/Hledger/Read/JournalReader.hs
+++ b/Hledger/Read/JournalReader.hs
@@ -63,8 +63,7 @@
   -- numberp,
   statusp,
   emptyorcommentlinep,
-  followingcommentp,
-  accountaliasp
+  followingcommentp
 
   -- * Tests
   ,tests_Hledger_Read_JournalReader
@@ -119,8 +118,18 @@
 
 -- | Parse and post-process a "Journal" from hledger's journal file
 -- format, or give an error.
-parse :: Maybe FilePath -> Bool -> FilePath -> Text -> ExceptT String IO Journal
-parse _ = parseAndFinaliseJournal journalp
+parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
+parse iopts = parseAndFinaliseJournal journalp' iopts
+  where
+    journalp' = do 
+      -- reverse parsed aliases to ensure that they are applied in order given on commandline
+      mapM_ addAccountAlias (reverse $ aliasesFromOpts iopts) 
+      journalp
+
+-- | Get the account name aliases from options, if any.
+aliasesFromOpts :: InputOpts -> [AccountAlias]
+aliasesFromOpts = map (\a -> fromparse $ runParser accountaliasp ("--alias "++quoteIfNeeded a) $ T.pack a)
+                  . aliases_
 
 --- * parsers
 --- ** journal
diff --git a/Hledger/Read/TimeclockReader.hs b/Hledger/Read/TimeclockReader.hs
--- a/Hledger/Read/TimeclockReader.hs
+++ b/Hledger/Read/TimeclockReader.hs
@@ -79,8 +79,8 @@
 -- | Parse and post-process a "Journal" from timeclock.el's timeclock
 -- format, saving the provided file path and the current time, or give an
 -- error.
-parse :: Maybe FilePath -> Bool -> FilePath -> Text -> ExceptT String IO Journal
-parse _ = parseAndFinaliseJournal timeclockfilep
+parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
+parse = parseAndFinaliseJournal timeclockfilep
 
 timeclockfilep :: ErroringJournalParser IO ParsedJournal
 timeclockfilep = do many timeclockitemp
diff --git a/Hledger/Read/TimedotReader.hs b/Hledger/Read/TimedotReader.hs
--- a/Hledger/Read/TimedotReader.hs
+++ b/Hledger/Read/TimedotReader.hs
@@ -65,8 +65,8 @@
   }
 
 -- | Parse and post-process a "Journal" from the timedot format, or give an error.
-parse :: Maybe FilePath -> Bool -> FilePath -> Text -> ExceptT String IO Journal
-parse _ = parseAndFinaliseJournal timedotfilep
+parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
+parse = parseAndFinaliseJournal timedotfilep
 
 timedotfilep :: JournalParser m ParsedJournal
 timedotfilep = do many timedotfileitemp
diff --git a/Hledger/Reports.hs b/Hledger/Reports.hs
--- a/Hledger/Reports.hs
+++ b/Hledger/Reports.hs
@@ -10,11 +10,13 @@
 
 module Hledger.Reports (
   module Hledger.Reports.ReportOptions,
+  module Hledger.Reports.ReportTypes,
   module Hledger.Reports.EntriesReport,
   module Hledger.Reports.PostingsReport,
   module Hledger.Reports.TransactionsReports,
   module Hledger.Reports.BalanceReport,
   module Hledger.Reports.MultiBalanceReports,
+  module Hledger.Reports.BudgetReport,
 --   module Hledger.Reports.BalanceHistoryReport,
 
   -- * Tests
@@ -25,11 +27,13 @@
 import Test.HUnit
 
 import Hledger.Reports.ReportOptions
+import Hledger.Reports.ReportTypes
 import Hledger.Reports.EntriesReport
 import Hledger.Reports.PostingsReport
 import Hledger.Reports.TransactionsReports
 import Hledger.Reports.BalanceReport
 import Hledger.Reports.MultiBalanceReports
+import Hledger.Reports.BudgetReport
 -- import Hledger.Reports.BalanceHistoryReport
 
 tests_Hledger_Reports :: Test
@@ -40,5 +44,6 @@
  tests_Hledger_Reports_EntriesReport,
  tests_Hledger_Reports_PostingsReport,
  tests_Hledger_Reports_BalanceReport,
- tests_Hledger_Reports_MultiBalanceReport
+ tests_Hledger_Reports_MultiBalanceReport,
+ tests_Hledger_Reports_BudgetReport
  ]
diff --git a/Hledger/Reports/BalanceReport.hs b/Hledger/Reports/BalanceReport.hs
--- a/Hledger/Reports/BalanceReport.hs
+++ b/Hledger/Reports/BalanceReport.hs
@@ -350,7 +350,7 @@
      ]
 
     ,"accounts report with cost basis" ~: do
-       j <- (readJournal Nothing Nothing Nothing $ unlines
+       j <- (readJournal def Nothing $ unlines
               [""
               ,"2008/1/1 test           "
               ,"  a:b          10h @ $50"
diff --git a/Hledger/Reports/BudgetReport.hs b/Hledger/Reports/BudgetReport.hs
new file mode 100644
--- /dev/null
+++ b/Hledger/Reports/BudgetReport.hs
@@ -0,0 +1,361 @@
+{- |
+-}
+
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Hledger.Reports.BudgetReport
+where
+
+import Data.Decimal
+import Data.List
+import Data.Maybe
+#if !(MIN_VERSION_base(4,11,0))
+import Data.Monoid ((<>))
+#endif
+import Data.Ord
+import Data.Time.Calendar
+import Safe
+import Test.HUnit
+--import Data.List
+--import Data.Maybe
+import qualified Data.Map as Map
+import Data.Map (Map)
+import qualified Data.Text as T
+--import qualified Data.Text.Lazy as TL
+--import System.Console.CmdArgs.Explicit as C
+--import Lucid as L
+--import Text.CSV
+--import Test.HUnit
+import Text.Printf (printf)
+import Text.Tabular as T
+--import Text.Tabular.AsciiWide
+
+import Hledger.Data
+--import Hledger.Query
+import Hledger.Utils
+--import Hledger.Read (mamountp')
+import Hledger.Reports.ReportOptions
+import Hledger.Reports.ReportTypes
+import Hledger.Reports.MultiBalanceReports
+
+
+--type MultiBalanceReportRow    = (AccountName, AccountName, Int, [MixedAmount], MixedAmount, MixedAmount)
+--type MultiBalanceReportTotals = ([MixedAmount], MixedAmount, MixedAmount) -- (Totals list, sum of totals, average of totals)
+
+--type PeriodicReportRow a =
+--  ( AccountName  -- ^ A full account name.
+--  , [a]          -- ^ The data value for each subperiod.
+--  , a            -- ^ The total of this row's values.
+--  , a            -- ^ The average of this row's values.
+--  )
+
+type BudgetGoal    = Change
+type BudgetTotal   = Total
+type BudgetAverage = Average
+
+-- | A budget report tracks expected and actual changes per account and subperiod.
+type BudgetReport = PeriodicReport (Maybe Change, Maybe BudgetGoal)
+
+-- | Calculate budget goals from all periodic transactions,
+-- actual balance changes from the regular transactions,
+-- and compare these to get a 'BudgetReport'.
+-- Unbudgeted accounts may be hidden or renamed (see budgetRollup).
+budgetReport :: ReportOpts -> Bool -> Bool -> DateSpan -> Day -> Journal -> BudgetReport
+budgetReport ropts assrt showunbudgeted reportspan d j =
+  let
+    q = queryFromOpts d ropts 
+    budgetedaccts = 
+      dbg2 "budgetedacctsinperiod" $
+      accountNamesFromPostings $ 
+      concatMap tpostings $ 
+      concatMap (flip runPeriodicTransaction reportspan) $ 
+      jperiodictxns j
+    actualj = dbg1 "actualj" $ budgetRollUp budgetedaccts showunbudgeted j
+    budgetj = dbg1 "budgetj" $ budgetJournal assrt ropts reportspan j
+    actualreport@(MultiBalanceReport (actualspans, _, _)) = dbg1 "actualreport" $ multiBalanceReport ropts  q actualj
+    budgetgoalreport@(MultiBalanceReport (_, budgetgoalitems, budgetgoaltotals)) = dbg1 "budgetgoalreport" $ multiBalanceReport ropts q budgetj
+    budgetgoalreport'
+      -- If no interval is specified:
+      -- budgetgoalreport's span might be shorter actualreport's due to periodic txns; 
+      -- it should be safe to replace it with the latter, so they combine well. 
+      | interval_ ropts == NoInterval = MultiBalanceReport (actualspans, budgetgoalitems, budgetgoaltotals)
+      | otherwise = budgetgoalreport 
+  in
+    dbg1 "budgetreport" $ combineBudgetAndActual budgetgoalreport' actualreport
+
+-- | Use all periodic transactions in the journal to generate 
+-- budget transactions in the specified report period.
+-- Budget transactions are similar to forecast transactions except
+-- their purpose is to set goal amounts (of change) per account and period.
+budgetJournal :: Bool -> ReportOpts -> DateSpan -> Journal -> Journal
+budgetJournal assrt _ropts reportspan j =
+  either error' id $ journalBalanceTransactions assrt j{ jtxns = budgetts }
+  where
+    budgetspan = dbg2 "budgetspan" $ reportspan
+    budgetts =
+      dbg1 "budgetts" $
+      [makeBudgetTxn t
+      | pt <- jperiodictxns j
+      , t <- runPeriodicTransaction pt budgetspan
+      ]
+    makeBudgetTxn t = txnTieKnot $ t { tdescription = T.pack "Budget transaction" }
+
+-- | Adjust a journal's account names for budget reporting, in two ways:
+--
+-- 1. accounts with no budget goal anywhere in their ancestry are moved 
+--    under the "unbudgeted" top level account.
+--
+-- 2. subaccounts with no budget goal are merged with their closest parent account
+--    with a budget goal, so that only budgeted accounts are shown. 
+--    This can be disabled by --show-unbudgeted.
+--
+budgetRollUp :: [AccountName] -> Bool -> Journal -> Journal
+budgetRollUp budgetedaccts showunbudgeted j = j { jtxns = remapTxn <$> jtxns j }
+  where
+    remapTxn = mapPostings (map remapPosting)
+      where
+        mapPostings f t = txnTieKnot $ t { tpostings = f $ tpostings t }
+        remapPosting p = p { paccount = remapAccount $ paccount p, porigin = Just . fromMaybe p $ porigin p }
+          where
+            remapAccount a
+              | hasbudget         = a
+              | hasbudgetedparent = if showunbudgeted then a else budgetedparent 
+              | otherwise         = if showunbudgeted then u <> acctsep <> a else u
+              where
+                hasbudget = a `elem` budgetedaccts
+                hasbudgetedparent = not $ T.null budgetedparent
+                budgetedparent = headDef "" $ filter (`elem` budgetedaccts) $ parentAccountNames a
+                u = unbudgetedAccountName
+
+-- | Combine a per-account-and-subperiod report of budget goals, and one
+-- of actual change amounts, into a budget performance report.
+-- The two reports should have the same report interval, but need not
+-- have exactly the same account rows or date columns.
+-- (Cells in the combined budget report can be missing a budget goal,
+-- an actual amount, or both.) The combined report will include:
+--
+-- - consecutive subperiods at the same interval as the two reports,
+--   spanning the period of both reports
+--
+-- - all accounts mentioned in either report, sorted by account code or
+--   account name or amount as appropriate.
+--
+combineBudgetAndActual :: MultiBalanceReport -> MultiBalanceReport -> BudgetReport
+combineBudgetAndActual
+  (MultiBalanceReport (budgetperiods, budgetrows, (budgettots, budgetgrandtot, budgetgrandavg)))
+  (MultiBalanceReport (actualperiods, actualrows, (actualtots, actualgrandtot, actualgrandavg))) =
+  let
+    periods = nub $ sort $ filter (/= nulldatespan) $ budgetperiods ++ actualperiods
+
+    -- first, combine any corresponding budget goals with actual changes
+    rows1 =
+      [ (acct, treeacct, treeindent, amtandgoals, totamtandgoal, avgamtandgoal)
+      | (acct, treeacct, treeindent, actualamts, actualtot, actualavg) <- actualrows
+      , let mbudgetgoals       = Map.lookup acct budgetGoalsByAcct :: Maybe ([BudgetGoal], BudgetTotal, BudgetAverage)
+      , let budgetmamts        = maybe (replicate (length periods) Nothing) (map Just . first3) mbudgetgoals :: [Maybe BudgetGoal]
+      , let mbudgettot         = maybe Nothing (Just . second3) mbudgetgoals :: Maybe BudgetTotal
+      , let mbudgetavg         = maybe Nothing (Just . third3)  mbudgetgoals :: Maybe BudgetAverage
+      , let acctBudgetByPeriod = Map.fromList [ (p,budgetamt) | (p, Just budgetamt) <- zip budgetperiods budgetmamts ] :: Map DateSpan BudgetGoal
+      , let acctActualByPeriod = Map.fromList [ (p,actualamt) | (p, Just actualamt) <- zip actualperiods (map Just actualamts) ] :: Map DateSpan Change
+      , let amtandgoals        = [ (Map.lookup p acctActualByPeriod, Map.lookup p acctBudgetByPeriod) | p <- periods ] :: [(Maybe Change, Maybe BudgetGoal)]
+      , let totamtandgoal      = (Just actualtot, mbudgettot)
+      , let avgamtandgoal      = (Just actualavg, mbudgetavg)
+      ]
+      where
+        budgetGoalsByAcct :: Map AccountName ([BudgetGoal], BudgetTotal, BudgetAverage) =
+          Map.fromList [ (acct, (amts, tot, avg)) | (acct, _, _, amts, tot, avg) <- budgetrows ]
+
+    -- next, make rows for budget goals with no actual changes
+    rows2 =
+      [ (acct, treeacct, treeindent, amtandgoals, totamtandgoal, avgamtandgoal)
+      | (acct, treeacct, treeindent, budgetgoals, budgettot, budgetavg) <- budgetrows
+      , not $ acct `elem` acctsdone
+      , let acctBudgetByPeriod = Map.fromList $ zip budgetperiods budgetgoals :: Map DateSpan BudgetGoal
+      , let amtandgoals        = [ (Nothing, Map.lookup p acctBudgetByPeriod) | p <- periods ] :: [(Maybe Change, Maybe BudgetGoal)]
+      , let totamtandgoal      = (Nothing, Just budgettot)
+      , let avgamtandgoal      = (Nothing, Just budgetavg)
+      ]
+      where
+        acctsdone = map first6 rows1
+
+    -- combine and re-sort rows
+    -- TODO: respect hierarchy in tree mode
+    -- TODO: respect --sort-amount
+    -- TODO: add --sort-budget to sort by budget goal amount
+    rows :: [PeriodicReportRow (Maybe Change, Maybe BudgetGoal)] =
+      sortBy (comparing first6) $ rows1 ++ rows2
+-- massive duplication from multiBalanceReport to handle tree mode sorting ?
+--      dbg1 "sorteditems" $
+--      sortitems items
+--      where
+--        sortitems
+--          | sort_amount_ opts && accountlistmode_ opts == ALTree       = sortTreeMultiBalanceReportRowsByAmount
+--          | sort_amount_ opts                                          = sortFlatMultiBalanceReportRowsByAmount
+--          | not (sort_amount_ opts) && accountlistmode_ opts == ALTree = sortTreeMultiBalanceReportRowsByAccountCodeAndName
+--          | otherwise                                                  = sortFlatMultiBalanceReportRowsByAccountCodeAndName
+--          where
+--            -- Sort the report rows, representing a flat account list, by row total.
+--            sortFlatMultiBalanceReportRowsByAmount = sortBy (maybeflip $ comparing fifth6)
+--              where
+--                maybeflip = if normalbalance_ opts == Just NormallyNegative then id else flip
+--
+--            -- Sort the report rows, representing a tree of accounts, by row total at each level.
+--            -- To do this we recreate an Account tree with the row totals as balances,
+--            -- so we can do a hierarchical sort, flatten again, and then reorder the
+--            -- report rows similarly. Yes this is pretty long winded.
+--            sortTreeMultiBalanceReportRowsByAmount rows = sortedrows
+--              where
+--                anamesandrows = [(first6 r, r) | r <- rows]
+--                anames = map fst anamesandrows
+--                atotals = [(a,tot) | (a,_,_,_,tot,_) <- rows]
+--                nametree = treeFromPaths $ map expandAccountName anames
+--                accounttree = nameTreeToAccount "root" nametree
+--                accounttreewithbals = mapAccounts setibalance accounttree
+--                  where
+--                    -- this error should not happen, but it's ugly TODO
+--                    setibalance a = a{aibalance=fromMaybe (error "sortTreeMultiBalanceReportRowsByAmount 1") $ lookup (aname a) atotals}
+--                sortedaccounttree = sortAccountTreeByAmount (fromMaybe NormallyPositive $ normalbalance_ opts) accounttreewithbals
+--                sortedaccounts = drop 1 $ flattenAccounts sortedaccounttree
+--                -- dropped the root account, also ignore any parent accounts not in rows
+--                sortedrows = concatMap (\a -> maybe [] (:[]) $ lookup (aname a) anamesandrows) sortedaccounts
+--
+--            -- Sort the report rows by account code if any, with the empty account code coming last, then account name.
+--            sortFlatMultiBalanceReportRowsByAccountCodeAndName = sortBy (comparing acodeandname)
+--              where
+--                acodeandname r = (acode', aname)
+--                  where
+--                    aname = first6 r
+--                    macode = fromMaybe Nothing $ lookup aname $ jaccounts j
+--                    acode' = fromMaybe maxBound macode
+--
+--            -- Sort the report rows, representing a tree of accounts, by account code and then account name at each level.
+--            -- Convert a tree of account names, look up the account codes, sort and flatten the tree, reorder the rows.
+--            sortTreeMultiBalanceReportRowsByAccountCodeAndName rows = sortedrows
+--              where
+--                anamesandrows = [(first6 r, r) | r <- rows]
+--                anames = map fst anamesandrows
+--                nametree = treeFromPaths $ map expandAccountName anames
+--                accounttree = nameTreeToAccount "root" nametree
+--                accounttreewithcodes = mapAccounts (accountSetCodeFrom j) accounttree
+--                sortedaccounttree = sortAccountTreeByAccountCodeAndName accounttreewithcodes
+--                sortedaccounts = drop 1 $ flattenAccounts sortedaccounttree
+--                -- dropped the root account, also ignore any parent accounts not in rows
+--                sortedrows = concatMap (\a -> maybe [] (:[]) $ lookup (aname a) anamesandrows) sortedaccounts
+--
+
+    -- TODO: grand total & average shows 0% when there are no actual amounts, inconsistent with other cells
+    totalrow =
+      ( ""
+      , ""
+      , 0
+      , [ (Map.lookup p totActualByPeriod, Map.lookup p totBudgetByPeriod) | p <- periods ] :: [(Maybe Total, Maybe BudgetTotal)]
+      , ( Just actualgrandtot, Just budgetgrandtot ) :: (Maybe Total, Maybe BudgetTotal)
+      , ( Just actualgrandavg, Just budgetgrandavg ) :: (Maybe Total, Maybe BudgetTotal)
+      )
+      where
+        totBudgetByPeriod = Map.fromList $ zip budgetperiods budgettots :: Map DateSpan BudgetTotal
+        totActualByPeriod = Map.fromList $ zip actualperiods actualtots :: Map DateSpan Change
+
+  in
+    PeriodicReport
+      ( periods
+      , rows
+      , totalrow
+      )
+
+-- | Figure out the overall period of a BudgetReport.
+budgetReportSpan :: BudgetReport -> DateSpan
+budgetReportSpan (PeriodicReport ([], _, _))    = DateSpan Nothing Nothing
+budgetReportSpan (PeriodicReport (spans, _, _)) = DateSpan (spanStart $ head spans) (spanEnd $ last spans)
+
+-- | Render a budget report as plain text suitable for console output.
+budgetReportAsText :: ReportOpts -> BudgetReport -> String
+budgetReportAsText ropts budgetr =
+  printf "Budget performance in %s:\n\n" (showDateSpan $ budgetReportSpan budgetr)
+  ++ 
+  tableAsText ropts showcell (budgetReportAsTable ropts budgetr)
+  where
+    -- XXX lay out actual, percentage and/or goal in the single table cell for now, should probably use separate cells
+    showcell :: (Maybe Change, Maybe BudgetGoal) -> String
+    showcell (mactual, mbudget) = actualstr ++ " " ++ budgetstr
+      where
+        actualwidth  = 7
+        percentwidth = 4
+        budgetwidth  = 5
+        actual = fromMaybe 0 mactual
+        actualstr = printf ("%"++show actualwidth++"s") (showamt actual)
+        budgetstr = case mbudget of
+          Nothing     -> replicate (percentwidth + 7 + budgetwidth) ' '
+          Just budget ->
+            case percentage actual budget of
+              Just pct ->
+                printf ("[%"++show percentwidth++"s%% of %"++show budgetwidth++"s]")
+                       (show $ roundTo 0 pct) (showbudgetamt budget)
+              Nothing ->
+                printf ("["++replicate (percentwidth+5) ' '++"%"++show budgetwidth++"s]")
+                       (showbudgetamt budget)
+
+    -- | Calculate the percentage of actual change to budget goal to show, if any.
+    -- Both amounts are converted to cost, if possible, before comparing.
+    -- A percentage will not be shown if:
+    -- - actual or goal are not the same, single, commodity
+    -- - the goal is zero
+    percentage :: Change -> BudgetGoal -> Maybe Percentage
+    percentage actual budget =
+      case (toCost actual, toCost budget) of
+        (Mixed [a], Mixed [b]) | (acommodity a == acommodity b || isZeroAmount a) && not (isZeroAmount b) 
+            -> Just $ 100 * aquantity a / aquantity b
+        _   -> Nothing
+      where
+        toCost = normaliseMixedAmount . costOfMixedAmount 
+
+    showamt :: MixedAmount -> String
+    showamt | color_ ropts  = cshowMixedAmountOneLineWithoutPrice
+            | otherwise     = showMixedAmountOneLineWithoutPrice
+
+    -- don't show the budget amount in color, it messes up alignment
+    showbudgetamt = showMixedAmountOneLineWithoutPrice
+
+-- | Build a 'Table' from a multi-column balance report.
+budgetReportAsTable :: ReportOpts -> BudgetReport -> Table String String (Maybe MixedAmount, Maybe MixedAmount)
+budgetReportAsTable 
+  ropts 
+  (PeriodicReport
+    ( periods
+    , rows
+    , (_, _, _, coltots, grandtot, grandavg)
+    )) =
+    addtotalrow $ 
+    Table
+      (T.Group NoLine $ map Header accts)
+      (T.Group NoLine $ map Header colheadings)
+      (map rowvals rows)
+  where
+    colheadings = map showDateSpanMonthAbbrev periods
+                  ++ (if row_total_ ropts then ["  Total"] else [])
+                  ++ (if average_   ropts then ["Average"] else [])
+    accts = map renderacct rows
+    renderacct (a,a',i,_,_,_)
+      | tree_ ropts = replicate ((i-1)*2) ' ' ++ T.unpack a'
+      | otherwise   = T.unpack $ maybeAccountNameDrop ropts a
+    rowvals (_,_,_,as,rowtot,rowavg) = as
+                                       ++ (if row_total_ ropts then [rowtot] else [])
+                                       ++ (if average_   ropts then [rowavg] else [])
+    addtotalrow | no_total_ ropts = id
+                | otherwise       = (+----+ (row "" $
+                                     coltots
+                                     ++ (if row_total_ ropts && not (null coltots) then [grandtot] else [])
+                                     ++ (if average_   ropts && not (null coltots) then [grandavg] else [])
+                                     ))
+
+-- XXX here for now
+-- | Drop leading components of accounts names as specified by --drop, but only in --flat mode.
+maybeAccountNameDrop :: ReportOpts -> AccountName -> AccountName
+maybeAccountNameDrop opts a | tree_ opts = a
+                            | otherwise  = accountNameDrop (drop_ opts) a
+
+tests_Hledger_Reports_BudgetReport :: Test
+tests_Hledger_Reports_BudgetReport = TestList [
+  ]
diff --git a/Hledger/Reports/MultiBalanceReports.hs b/Hledger/Reports/MultiBalanceReports.hs
--- a/Hledger/Reports/MultiBalanceReports.hs
+++ b/Hledger/Reports/MultiBalanceReports.hs
@@ -12,6 +12,8 @@
   balanceReportFromMultiBalanceReport,
   mbrNegate,
   mbrNormaliseSign,
+  multiBalanceReportSpan,
+  tableAsText,
 
   -- -- * Tests
   tests_Hledger_Reports_MultiBalanceReport
@@ -24,6 +26,8 @@
 import Data.Time.Calendar
 import Safe
 import Test.HUnit
+import Text.Tabular as T
+import Text.Tabular.AsciiWide
 
 import Hledger.Data
 import Hledger.Query
@@ -259,6 +263,11 @@
     mbrRowNegate (acct,shortacct,indent,amts,tot,avg) = (acct,shortacct,indent,map negate amts,-tot,-avg)
     mbrTotalsRowNegate (amts,tot,avg) = (map negate amts,-tot,-avg)
 
+-- | Figure out the overall date span of a multicolumn balance report.
+multiBalanceReportSpan :: MultiBalanceReport -> DateSpan
+multiBalanceReportSpan (MultiBalanceReport ([], _, _))       = DateSpan Nothing Nothing
+multiBalanceReportSpan (MultiBalanceReport (colspans, _, _)) = DateSpan (spanStart $ head colspans) (spanEnd $ last colspans)
+
 -- | Generates a simple non-columnar BalanceReport, but using multiBalanceReport, 
 -- in order to support --historical. Does not support tree-mode boring parent eliding. 
 -- If the normalbalance_ option is set, it adjusts the sorting and sign of amounts 
@@ -321,6 +330,22 @@
       ],
       Mixed [usd0])
   ]
+
+-- common rendering helper, XXX here for now
+
+tableAsText :: ReportOpts -> (a -> String) -> Table String String a -> String
+tableAsText (ReportOpts{pretty_tables_ = pretty}) showcell =
+  unlines
+  . trimborder
+  . lines
+  . render pretty id id showcell
+  . align
+  where
+    trimborder = drop 1 . init . map (drop 1 . init)
+    align (Table l t d) = Table l' t d
+      where
+        acctswidth = maximum' $ map strWidth (headerContents l)
+        l'         = padRightWide acctswidth <$> l
 
 tests_Hledger_Reports_MultiBalanceReport :: Test
 tests_Hledger_Reports_MultiBalanceReport = TestList
diff --git a/Hledger/Reports/ReportOptions.hs b/Hledger/Reports/ReportOptions.hs
--- a/Hledger/Reports/ReportOptions.hs
+++ b/Hledger/Reports/ReportOptions.hs
@@ -115,7 +115,6 @@
       --   normally positive for a more conventional display.   
     ,color_          :: Bool
     ,forecast_       :: Bool
-    ,auto_           :: Bool
  } deriving (Show, Data, Typeable)
 
 instance Default ReportOpts where def = defreportopts
@@ -148,7 +147,6 @@
     def
     def
     def
-    def
 
 rawOptsToReportOpts :: RawOpts -> IO ReportOpts
 rawOptsToReportOpts rawopts = checkReportOpts <$> do
@@ -181,7 +179,6 @@
     ,pretty_tables_ = boolopt "pretty-tables" rawopts'
     ,color_       = color
     ,forecast_    = boolopt "forecast" rawopts'
-    ,auto_        = boolopt "auto" rawopts'
     }
 
 -- | Do extra validation of raw option values, raising an error if there's a problem.
diff --git a/Hledger/Reports/ReportTypes.hs b/Hledger/Reports/ReportTypes.hs
new file mode 100644
--- /dev/null
+++ b/Hledger/Reports/ReportTypes.hs
@@ -0,0 +1,40 @@
+{- |
+New common report types, used by the BudgetReport for now, perhaps all reports later.
+-}
+
+module Hledger.Reports.ReportTypes
+where
+
+import Data.Decimal
+import Hledger.Data
+
+type Percentage = Decimal
+
+type Change  = MixedAmount  -- ^ A change in balance during a certain period.
+type Balance = MixedAmount  -- ^ An ending balance as of some date.
+type Total   = MixedAmount  -- ^ The sum of 'Change's in a report or a report row. Does not make sense for 'Balance's.
+type Average = MixedAmount  -- ^ The average of 'Change's or 'Balance's in a report or report row.
+
+-- | A generic tabular report of some value, where each row corresponds to an account
+-- and each column is a date period. The column periods are usually consecutive subperiods
+-- formed by splitting the overall report period by some report interval (daily, weekly, etc.)
+-- Depending on the value type, this can be a report of balance changes, ending balances,
+-- budget performance, etc. Successor to MultiBalanceReport.
+data PeriodicReport a =
+  PeriodicReport
+    ( [DateSpan]            -- The subperiods formed by splitting the overall report period by the report interval.
+                            -- For ending-balance reports, only the end date is significant.
+                            -- Usually displayed as report columns.
+    , [PeriodicReportRow a] -- One row per account in the report.
+    , PeriodicReportRow a   -- The grand totals row. The account name in this row is always empty.
+    )
+   deriving (Show)
+
+type PeriodicReportRow a =
+  ( AccountName  -- A full account name.
+  , AccountName  -- Shortened form of the account name to display in tree mode. Usually the leaf name, possibly with parent accounts prefixed.
+  , Int          -- Indent level for displaying this account name in tree mode. 0, 1, 2... 
+  , [a]          -- The data value for each subperiod.
+  , a            -- The total of this row's values.
+  , a            -- The average of this row's values.
+  )
diff --git a/Text/Tabular/AsciiWide.hs b/Text/Tabular/AsciiWide.hs
new file mode 100644
--- /dev/null
+++ b/Text/Tabular/AsciiWide.hs
@@ -0,0 +1,111 @@
+-- | Text.Tabular.AsciiArt from tabular-0.2.2.7, modified to treat
+-- wide characters as double width.
+
+module Text.Tabular.AsciiWide where
+
+import Data.List (intersperse, transpose)
+import Text.Tabular
+import Hledger.Utils.String
+
+-- | for simplicity, we assume that each cell is rendered
+--   on a single line
+render :: Bool -- ^ pretty tables
+       -> (rh -> String)
+       -> (ch -> String)
+       -> (a -> String)
+       -> Table rh ch a
+       -> String
+render pretty fr fc f (Table rh ch cells) =
+  unlines $ [ bar SingleLine   -- +--------------------------------------+
+            , renderColumns pretty sizes ch2
+            , bar DoubleLine   -- +======================================+
+            ] ++
+            (renderRs $ fmap renderR $ zipHeader [] cells $ fmap fr rh) ++
+            [ bar SingleLine ] -- +--------------------------------------+
+ where
+  bar = concat . renderHLine pretty sizes ch2
+  -- ch2 and cell2 include the row and column labels
+  ch2 = Group DoubleLine [Header "", fmap fc ch]
+  cells2 = headerContents ch2
+         : zipWith (\h cs -> h : map f cs) rhStrings cells
+  --
+  renderR (cs,h) = renderColumns pretty sizes $ Group DoubleLine
+                    [ Header h
+                    , fmap fst $ zipHeader "" (map f cs) ch]
+  rhStrings = map fr $ headerContents rh
+  -- maximum width for each column
+  sizes   = map (maximum . map strWidth) . transpose $ cells2
+  renderRs (Header s)   = [s]
+  renderRs (Group p hs) = concat . intersperse sep . map renderRs $ hs
+    where sep = renderHLine pretty sizes ch2 p
+
+verticalBar :: Bool -> Char
+verticalBar pretty = if pretty then '│' else '|'
+
+leftBar :: Bool -> String
+leftBar pretty = verticalBar pretty : " "
+
+rightBar :: Bool -> String
+rightBar pretty = " " ++ [verticalBar pretty]
+
+midBar :: Bool -> String
+midBar pretty = " " ++ verticalBar pretty : " "
+
+doubleMidBar :: Bool -> String
+doubleMidBar pretty = if pretty then " ║ " else " || "
+
+horizontalBar :: Bool -> Char
+horizontalBar pretty = if pretty then '─' else '-'
+
+doubleHorizontalBar :: Bool -> Char
+doubleHorizontalBar pretty = if pretty then '═' else '='
+
+-- | We stop rendering on the shortest list!
+renderColumns :: Bool -- ^ pretty
+              -> [Int] -- ^ max width for each column
+              -> Header String
+              -> String
+renderColumns pretty is h = leftBar pretty ++ coreLine ++ rightBar pretty
+ where
+  coreLine = concatMap helper $ flattenHeader $ zipHeader 0 is h
+  helper = either hsep (uncurry padLeftWide)
+  hsep :: Properties -> String
+  hsep NoLine     = "  "
+  hsep SingleLine = midBar pretty
+  hsep DoubleLine = doubleMidBar pretty
+
+renderHLine :: Bool -- ^ pretty
+            -> [Int] -- ^ width specifications
+            -> Header String
+            -> Properties
+            -> [String]
+renderHLine _ _ _ NoLine = []
+renderHLine pretty w h SingleLine = [renderHLine' pretty SingleLine w (horizontalBar pretty) h]
+renderHLine pretty w h DoubleLine = [renderHLine' pretty DoubleLine w (doubleHorizontalBar pretty) h]
+
+doubleCross :: Bool -> String
+doubleCross pretty = if pretty then "╬" else "++"
+
+doubleVerticalCross :: Bool -> String
+doubleVerticalCross pretty = if pretty then "╫" else "++"
+
+cross :: Bool -> Char
+cross pretty = if pretty then '┼' else '+'
+
+renderHLine' :: Bool -> Properties -> [Int] -> Char -> Header String -> String
+renderHLine' pretty prop is sep h = [ cross pretty, sep ] ++ coreLine ++ [sep, cross pretty]
+ where
+  coreLine        = concatMap helper $ flattenHeader $ zipHeader 0 is h
+  helper          = either vsep dashes
+  dashes (i,_)    = replicate i sep
+  vsep NoLine     = replicate 2 sep  -- match the double space sep in renderColumns 
+  vsep SingleLine = sep : cross pretty : [sep]
+  vsep DoubleLine = sep : cross' ++ [sep]
+  cross' = case prop of
+     DoubleLine -> doubleCross pretty
+     _ -> doubleVerticalCross pretty
+
+-- padLeft :: Int -> String -> String
+-- padLeft l s = padding ++ s
+--  where padding = replicate (l - length s) ' '
+
diff --git a/hledger-lib.cabal b/hledger-lib.cabal
--- a/hledger-lib.cabal
+++ b/hledger-lib.cabal
@@ -1,11 +1,11 @@
--- This file has been generated from package.yaml by hpack version 0.20.0.
+-- This file has been generated from package.yaml by hpack version 0.28.2.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 151fb38a89818af88d66f068ef87e0af6f8497b1ef11ab825558b8147952c88d
+-- hash: 1905b347a666e216347e595c5fcb0b340e5618a383a5493438200c6bcd5e6f98
 
 name:           hledger-lib
-version:        1.9
+version:        1.9.1
 synopsis:       Core data types, parsers and functionality for the hledger accounting tools
 description:    This is a reusable library containing hledger's core functionality.
                 .
@@ -26,7 +26,6 @@
 tested-with:    GHC==7.10.3, GHC==8.0.2, GHC==8.2.1
 build-type:     Simple
 cabal-version:  >= 1.10
-
 extra-source-files:
     CHANGES
     hledger_csv.5
@@ -48,44 +47,6 @@
   location: https://github.com/simonmichael/hledger
 
 library
-  hs-source-dirs:
-      ./.
-  ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
-  build-depends:
-      Decimal
-    , HUnit
-    , ansi-terminal >=0.6.2.3
-    , array
-    , base >=4.8 && <4.12
-    , base-compat >=0.8.1
-    , blaze-markup >=0.5.1
-    , bytestring
-    , cmdargs >=0.10
-    , containers
-    , csv
-    , data-default >=0.5
-    , deepseq
-    , directory
-    , extra
-    , filepath
-    , hashtables >=1.2
-    , megaparsec >=5.0
-    , mtl
-    , mtl-compat
-    , old-time
-    , parsec >=3
-    , pretty-show >=1.6.4
-    , regex-tdfa
-    , safe >=0.2
-    , split >=0.1
-    , text >=1.2
-    , time >=1.5
-    , transformers >=0.2
-    , uglymemo
-    , utf8-string >=0.3.5
-  if (!impl(ghc >= 8.0))
-    build-depends:
-        semigroups ==0.18.*
   exposed-modules:
       Hledger
       Hledger.Data
@@ -114,8 +75,10 @@
       Hledger.Read.TimeclockReader
       Hledger.Reports
       Hledger.Reports.ReportOptions
+      Hledger.Reports.ReportTypes
       Hledger.Reports.BalanceHistoryReport
       Hledger.Reports.BalanceReport
+      Hledger.Reports.BudgetReport
       Hledger.Reports.EntriesReport
       Hledger.Reports.MultiBalanceReports
       Hledger.Reports.PostingsReport
@@ -131,20 +94,14 @@
       Hledger.Utils.Tree
       Hledger.Utils.UTF8IOCompat
       Text.Megaparsec.Compat
+      Text.Tabular.AsciiWide
   other-modules:
       Paths_hledger_lib
-  default-language: Haskell2010
-
-test-suite doctests
-  type: exitcode-stdio-1.0
-  main-is: doctests.hs
   hs-source-dirs:
       ./.
-      tests
   ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
   build-depends:
       Decimal
-    , Glob >=0.7
     , HUnit
     , ansi-terminal >=0.6.2.3
     , array
@@ -158,7 +115,6 @@
     , data-default >=0.5
     , deepseq
     , directory
-    , doctest >=0.8
     , extra
     , filepath
     , hashtables >=1.2
@@ -171,6 +127,7 @@
     , regex-tdfa
     , safe >=0.2
     , split >=0.1
+    , tabular >=0.2
     , text >=1.2
     , time >=1.5
     , transformers >=0.2
@@ -179,8 +136,11 @@
   if (!impl(ghc >= 8.0))
     build-depends:
         semigroups ==0.18.*
-  if impl(ghc >= 8.4) && os(darwin)
-    buildable: False
+  default-language: Haskell2010
+
+test-suite doctests
+  type: exitcode-stdio-1.0
+  main-is: doctests.hs
   other-modules:
       Hledger
       Hledger.Data
@@ -210,10 +170,12 @@
       Hledger.Reports
       Hledger.Reports.BalanceHistoryReport
       Hledger.Reports.BalanceReport
+      Hledger.Reports.BudgetReport
       Hledger.Reports.EntriesReport
       Hledger.Reports.MultiBalanceReports
       Hledger.Reports.PostingsReport
       Hledger.Reports.ReportOptions
+      Hledger.Reports.ReportTypes
       Hledger.Reports.TransactionsReports
       Hledger.Utils
       Hledger.Utils.Color
@@ -226,18 +188,15 @@
       Hledger.Utils.Tree
       Hledger.Utils.UTF8IOCompat
       Text.Megaparsec.Compat
+      Text.Tabular.AsciiWide
       Paths_hledger_lib
-  default-language: Haskell2010
-
-test-suite easytests
-  type: exitcode-stdio-1.0
-  main-is: easytests.hs
   hs-source-dirs:
       ./.
       tests
   ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
   build-depends:
       Decimal
+    , Glob >=0.7
     , HUnit
     , ansi-terminal >=0.6.2.3
     , array
@@ -251,11 +210,10 @@
     , data-default >=0.5
     , deepseq
     , directory
-    , easytest
+    , doctest >=0.8
     , extra
     , filepath
     , hashtables >=1.2
-    , hledger-lib
     , megaparsec >=5.0
     , mtl
     , mtl-compat
@@ -265,6 +223,7 @@
     , regex-tdfa
     , safe >=0.2
     , split >=0.1
+    , tabular >=0.2
     , text >=1.2
     , time >=1.5
     , transformers >=0.2
@@ -273,6 +232,13 @@
   if (!impl(ghc >= 8.0))
     build-depends:
         semigroups ==0.18.*
+  if impl(ghc >= 8.4) && os(darwin)
+    buildable: False
+  default-language: Haskell2010
+
+test-suite easytests
+  type: exitcode-stdio-1.0
+  main-is: easytests.hs
   other-modules:
       Hledger
       Hledger.Data
@@ -302,10 +268,12 @@
       Hledger.Reports
       Hledger.Reports.BalanceHistoryReport
       Hledger.Reports.BalanceReport
+      Hledger.Reports.BudgetReport
       Hledger.Reports.EntriesReport
       Hledger.Reports.MultiBalanceReports
       Hledger.Reports.PostingsReport
       Hledger.Reports.ReportOptions
+      Hledger.Reports.ReportTypes
       Hledger.Reports.TransactionsReports
       Hledger.Utils
       Hledger.Utils.Color
@@ -318,12 +286,8 @@
       Hledger.Utils.Tree
       Hledger.Utils.UTF8IOCompat
       Text.Megaparsec.Compat
+      Text.Tabular.AsciiWide
       Paths_hledger_lib
-  default-language: Haskell2010
-
-test-suite hunittests
-  type: exitcode-stdio-1.0
-  main-is: hunittests.hs
   hs-source-dirs:
       ./.
       tests
@@ -343,6 +307,7 @@
     , data-default >=0.5
     , deepseq
     , directory
+    , easytest
     , extra
     , filepath
     , hashtables >=1.2
@@ -356,8 +321,7 @@
     , regex-tdfa
     , safe >=0.2
     , split >=0.1
-    , test-framework
-    , test-framework-hunit
+    , tabular >=0.2
     , text >=1.2
     , time >=1.5
     , transformers >=0.2
@@ -366,6 +330,11 @@
   if (!impl(ghc >= 8.0))
     build-depends:
         semigroups ==0.18.*
+  default-language: Haskell2010
+
+test-suite hunittests
+  type: exitcode-stdio-1.0
+  main-is: hunittests.hs
   other-modules:
       Hledger
       Hledger.Data
@@ -395,10 +364,12 @@
       Hledger.Reports
       Hledger.Reports.BalanceHistoryReport
       Hledger.Reports.BalanceReport
+      Hledger.Reports.BudgetReport
       Hledger.Reports.EntriesReport
       Hledger.Reports.MultiBalanceReports
       Hledger.Reports.PostingsReport
       Hledger.Reports.ReportOptions
+      Hledger.Reports.ReportTypes
       Hledger.Reports.TransactionsReports
       Hledger.Utils
       Hledger.Utils.Color
@@ -411,5 +382,49 @@
       Hledger.Utils.Tree
       Hledger.Utils.UTF8IOCompat
       Text.Megaparsec.Compat
+      Text.Tabular.AsciiWide
       Paths_hledger_lib
+  hs-source-dirs:
+      ./.
+      tests
+  ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
+  build-depends:
+      Decimal
+    , HUnit
+    , ansi-terminal >=0.6.2.3
+    , array
+    , base >=4.8 && <4.12
+    , base-compat >=0.8.1
+    , blaze-markup >=0.5.1
+    , bytestring
+    , cmdargs >=0.10
+    , containers
+    , csv
+    , data-default >=0.5
+    , deepseq
+    , directory
+    , extra
+    , filepath
+    , hashtables >=1.2
+    , hledger-lib
+    , megaparsec >=5.0
+    , mtl
+    , mtl-compat
+    , old-time
+    , parsec >=3
+    , pretty-show >=1.6.4
+    , regex-tdfa
+    , safe >=0.2
+    , split >=0.1
+    , tabular >=0.2
+    , test-framework
+    , test-framework-hunit
+    , text >=1.2
+    , time >=1.5
+    , transformers >=0.2
+    , uglymemo
+    , utf8-string >=0.3.5
+  if (!impl(ghc >= 8.0))
+    build-depends:
+        semigroups ==0.18.*
   default-language: Haskell2010
diff --git a/hledger_csv.5 b/hledger_csv.5
--- a/hledger_csv.5
+++ b/hledger_csv.5
@@ -1,5 +1,5 @@
 
-.TH "hledger_csv" "5" "March 2018" "hledger 1.9" "hledger User Manuals"
+.TH "hledger_csv" "5" "April 2018" "hledger 1.9.1" "hledger User Manuals"
 
 
 
diff --git a/hledger_csv.info b/hledger_csv.info
--- a/hledger_csv.info
+++ b/hledger_csv.info
@@ -3,8 +3,8 @@
 
 File: hledger_csv.info,  Node: Top,  Next: CSV RULES,  Up: (dir)
 
-hledger_csv(5) hledger 1.9
-**************************
+hledger_csv(5) hledger 1.9.1
+****************************
 
 hledger can read CSV (comma-separated value) files as if they were
 journal files, automatically converting each CSV record into a
@@ -317,33 +317,33 @@
 
 Tag Table:
 Node: Top72
-Node: CSV RULES2161
-Ref: #csv-rules2269
-Node: skip2531
-Ref: #skip2625
-Node: date-format2797
-Ref: #date-format2924
-Node: field list3430
-Ref: #field-list3567
-Node: field assignment4272
-Ref: #field-assignment4427
-Node: conditional block4931
-Ref: #conditional-block5085
-Node: include5981
-Ref: #include6111
-Node: newest-first6342
-Ref: #newest-first6456
-Node: CSV TIPS6867
-Ref: #csv-tips6961
-Node: CSV ordering7079
-Ref: #csv-ordering7197
-Node: CSV accounts7378
-Ref: #csv-accounts7516
-Node: CSV amounts7770
-Ref: #csv-amounts7916
-Node: CSV balance assertions8691
-Ref: #csv-balance-assertions8873
-Node: Reading multiple CSV files9078
-Ref: #reading-multiple-csv-files9248
+Node: CSV RULES2165
+Ref: #csv-rules2273
+Node: skip2535
+Ref: #skip2629
+Node: date-format2801
+Ref: #date-format2928
+Node: field list3434
+Ref: #field-list3571
+Node: field assignment4276
+Ref: #field-assignment4431
+Node: conditional block4935
+Ref: #conditional-block5089
+Node: include5985
+Ref: #include6115
+Node: newest-first6346
+Ref: #newest-first6460
+Node: CSV TIPS6871
+Ref: #csv-tips6965
+Node: CSV ordering7083
+Ref: #csv-ordering7201
+Node: CSV accounts7382
+Ref: #csv-accounts7520
+Node: CSV amounts7774
+Ref: #csv-amounts7920
+Node: CSV balance assertions8695
+Ref: #csv-balance-assertions8877
+Node: Reading multiple CSV files9082
+Ref: #reading-multiple-csv-files9252
 
 End Tag Table
diff --git a/hledger_csv.txt b/hledger_csv.txt
--- a/hledger_csv.txt
+++ b/hledger_csv.txt
@@ -249,4 +249,4 @@
 
 
 
-hledger 1.9                       March 2018                    hledger_csv(5)
+hledger 1.9.1                     April 2018                    hledger_csv(5)
diff --git a/hledger_journal.5 b/hledger_journal.5
--- a/hledger_journal.5
+++ b/hledger_journal.5
@@ -1,6 +1,6 @@
 .\"t
 
-.TH "hledger_journal" "5" "March 2018" "hledger 1.9" "hledger User Manuals"
+.TH "hledger_journal" "5" "April 2018" "hledger 1.9.1" "hledger User Manuals"
 
 
 
@@ -758,11 +758,6 @@
 (Star comments cause org\-mode nodes to be ignored, allowing emacs users
 to fold and navigate their journals with org\-mode or orgstruct\-mode.)
 .PP
-Also, anything between \f[C]comment\f[] and \f[C]end\ comment\f[]
-directives is a (multi\-line) comment.
-If there is no \f[C]end\ comment\f[], the comment extends to the end of
-the file.
-.PP
 You can attach comments to a transaction by writing them after the
 description and/or indented on the following lines (before the
 postings).
@@ -795,6 +790,9 @@
 ;\ a\ file\ comment\ (because\ not\ indented)
 \f[]
 .fi
+.PP
+You can also comment larger regions of a file using \f[C]comment\f[] and
+\f[C]end\ comment\f[] directives.
 .SS Tags
 .PP
 Tags are a way to add extra labels or labelled data to postings and
@@ -855,8 +853,174 @@
 Tags are like Ledger's metadata feature, except hledger's tag values are
 simple strings.
 .SS Directives
-.SS Account aliases
+.SS Comment blocks
 .PP
+A line containing just \f[C]comment\f[] starts a commented region of the
+file, and a line containing just \f[C]end\ comment\f[] (or the end of
+the current file) ends it.
+See also comments.
+.SS Including other files
+.PP
+You can pull in the content of additional files by writing an include
+directive, like this:
+.IP
+.nf
+\f[C]
+include\ path/to/file.journal
+\f[]
+.fi
+.PP
+If the path does not begin with a slash, it is relative to the current
+file.
+Glob patterns (\f[C]*\f[]) are not currently supported.
+.PP
+The \f[C]include\f[] directive can only be used in journal files.
+It can include journal, timeclock or timedot files, but not CSV files.
+.SS Default year
+.PP
+You can set a default year to be used for subsequent dates which don't
+specify a year.
+This is a line beginning with \f[C]Y\f[] followed by the year.
+Eg:
+.IP
+.nf
+\f[C]
+Y2009\ \ \ \ \ \ ;\ set\ default\ year\ to\ 2009
+
+12/15\ \ \ \ \ \ ;\ equivalent\ to\ 2009/12/15
+\ \ expenses\ \ 1
+\ \ assets
+
+Y2010\ \ \ \ \ \ ;\ change\ default\ year\ to\ 2010
+
+2009/1/30\ \ ;\ specifies\ the\ year,\ not\ affected
+\ \ expenses\ \ 1
+\ \ assets
+
+1/31\ \ \ \ \ \ \ ;\ equivalent\ to\ 2010/1/31
+\ \ expenses\ \ 1
+\ \ assets
+\f[]
+.fi
+.SS Declaring commodities
+.PP
+The \f[C]commodity\f[] directive declares commodities which may be used
+in the journal (though currently we do not enforce this).
+It may be written on a single line, like this:
+.IP
+.nf
+\f[C]
+;\ commodity\ EXAMPLEAMOUNT
+
+;\ display\ AAAA\ amounts\ with\ the\ symbol\ on\ the\ right,\ space\-separated,
+;\ using\ period\ as\ decimal\ point,\ with\ four\ decimal\ places,\ and
+;\ separating\ thousands\ with\ comma.
+commodity\ 1,000.0000\ AAAA
+\f[]
+.fi
+.PP
+or on multiple lines, using the \[lq]format\[rq] subdirective.
+In this case the commodity symbol appears twice and should be the same
+in both places:
+.IP
+.nf
+\f[C]
+;\ commodity\ SYMBOL
+;\ \ \ format\ EXAMPLEAMOUNT
+
+;\ display\ indian\ rupees\ with\ currency\ name\ on\ the\ left,
+;\ thousands,\ lakhs\ and\ crores\ comma\-separated,
+;\ period\ as\ decimal\ point,\ and\ two\ decimal\ places.
+commodity\ INR
+\ \ format\ INR\ 9,99,99,999.00
+\f[]
+.fi
+.PP
+Commodity directives have a second purpose: they define the standard
+display format for amounts in the commodity.
+Normally the display format is inferred from journal entries, but this
+can be unpredictable; declaring it with a commodity directive overrides
+this and removes ambiguity.
+Towards this end, amounts in commodity directives must always be written
+with a decimal point (a period or comma, followed by 0 or more decimal
+digits).
+.SS Default commodity
+.PP
+The D directive sets a default commodity (and display format), to be
+used for amounts without a commodity symbol (ie, plain numbers).
+(Note this differs from Ledger's default commodity directive.) The
+commodity and display format will be applied to all subsequent
+commodity\-less amounts, or until the next D directive.
+.IP
+.nf
+\f[C]
+#\ commodity\-less\ amounts\ should\ be\ treated\ as\ dollars
+#\ (and\ displayed\ with\ symbol\ on\ the\ left,\ thousands\ separators\ and\ two\ decimal\ places)
+D\ $1,000.00
+
+1/1
+\ \ a\ \ \ \ \ 5\ \ \ \ ;\ <\-\ commodity\-less\ amount,\ becomes\ $1
+\ \ b
+\f[]
+.fi
+.PP
+As with the \f[C]commodity\f[] directive, the amount must always be
+written with a decimal point.
+.SS Declaring accounts
+.PP
+The \f[C]account\f[] directive predeclares account names.
+The simplest form is \f[C]account\ ACCTNAME\f[], eg:
+.IP
+.nf
+\f[C]
+account\ assets:bank:checking
+\f[]
+.fi
+.PP
+Currently this mainly helps with account name autocompletion in eg
+hledger add, hledger\-iadd, hledger\-web, and ledger\-mode.
+.PD 0
+.P
+.PD
+In future it will also help detect misspelled accounts.
+.PP
+Account names can be followed by a numeric account code:
+.IP
+.nf
+\f[C]
+account\ assets\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 1000
+account\ assets:bank:checking\ \ \ \ 1110
+account\ liabilities\ \ \ \ \ \ \ \ \ \ \ \ \ 2000
+account\ revenues\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 4000
+account\ expenses\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 6000
+\f[]
+.fi
+.PP
+This affects account display order in reports: accounts with codes are
+listed before accounts without codes, in increasing code order.
+(Otherwise, accounts are listed alphabetically.) Account codes should be
+all numeric digits, unique, and separated from the account name by at
+least two spaces (since account names may contain single spaces).
+By convention, often the first digit indicates the type of account, as
+in this numbering scheme and the example above.
+In future, we might use this to recognize account types.
+.PP
+An account directive can also have indented subdirectives following it,
+which are currently ignored.
+Here is the full syntax:
+.IP
+.nf
+\f[C]
+;\ account\ ACCTNAME\ \ [OPTIONALCODE]
+;\ \ \ [OPTIONALSUBDIRECTIVES]
+
+account\ assets:bank:checking\ \ \ 1110
+\ \ a\ comment
+\ \ some\-tag:12345
+\f[]
+.fi
+.SS Rewriting accounts
+.PP
 You can define aliases which rewrite your account names (after reading
 the journal, before generating reports).
 hledger's account aliases can be useful for:
@@ -871,7 +1035,7 @@
 .IP \[bu] 2
 customising reports
 .PP
-See also Cookbook: rewrite account names.
+See also Cookbook: Rewrite account names.
 .SS Basic aliases
 .PP
 To set an account alias, use the \f[C]alias\f[] directive in your
@@ -946,7 +1110,7 @@
 precedence over earlier ones; directives not yet seen are ignored)
 .IP "2." 3
 alias options, in the order they appear on the command line
-.SS end aliases
+.SS \f[C]end\ aliases\f[]
 .PP
 You can clear (forget) all currently defined aliases with the
 \f[C]end\ aliases\f[] directive:
@@ -956,60 +1120,7 @@
 end\ aliases
 \f[]
 .fi
-.SS account directive
-.PP
-The \f[C]account\f[] directive predeclares account names.
-The simplest form is \f[C]account\ ACCTNAME\f[], eg:
-.IP
-.nf
-\f[C]
-account\ assets:bank:checking
-\f[]
-.fi
-.PP
-Currently this mainly helps with account name autocompletion in eg
-hledger add, hledger\-iadd, hledger\-web, and ledger\-mode.
-.PD 0
-.P
-.PD
-In future it will also help detect misspelled accounts.
-.PP
-Account names can be followed by a numeric account code:
-.IP
-.nf
-\f[C]
-account\ assets\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 1000
-account\ assets:bank:checking\ \ \ \ 1110
-account\ liabilities\ \ \ \ \ \ \ \ \ \ \ \ \ 2000
-account\ revenues\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 4000
-account\ expenses\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 6000
-\f[]
-.fi
-.PP
-This affects account display order in reports: accounts with codes are
-listed before accounts without codes, in increasing code order.
-(Otherwise, accounts are listed alphabetically.) Account codes should be
-all numeric digits, unique, and separated from the account name by at
-least two spaces (since account names may contain single spaces).
-By convention, often the first digit indicates the type of account, as
-in this numbering scheme and the example above.
-In future, we might use this to recognize account types.
-.PP
-An account directive can also have indented subdirectives following it,
-which are currently ignored.
-Here is the full syntax:
-.IP
-.nf
-\f[C]
-;\ account\ ACCTNAME\ \ [OPTIONALCODE]
-;\ \ \ [OPTIONALSUBDIRECTIVES]
-
-account\ assets:bank:checking\ \ \ 1110
-\ \ a\ comment
-\ \ some\-tag:12345
-\f[]
-.fi
-.SS apply account directive
+.SS Default parent account
 .PP
 You can specify a parent account which will be prepended to all accounts
 within a section of the journal.
@@ -1054,116 +1165,13 @@
 .PP
 Prior to hledger 1.0, legacy \f[C]account\f[] and \f[C]end\f[] spellings
 were also supported.
-.SS Multi\-line comments
-.PP
-A line containing just \f[C]comment\f[] starts a multi\-line comment,
-and a line containing just \f[C]end\ comment\f[] ends it.
-See comments.
-.SS commodity directive
-.PP
-The \f[C]commodity\f[] directive predefines commodities (currently this
-is just informational), and also it may define the display format for
-amounts in this commodity (overriding the automatically inferred
-format).
-.PP
-It may be written on a single line, like this:
-.IP
-.nf
-\f[C]
-;\ commodity\ EXAMPLEAMOUNT
-
-;\ display\ AAAA\ amounts\ with\ the\ symbol\ on\ the\ right,\ space\-separated,
-;\ using\ period\ as\ decimal\ point,\ with\ four\ decimal\ places,\ and
-;\ separating\ thousands\ with\ comma.
-commodity\ 1,000.0000\ AAAA
-\f[]
-.fi
-.PP
-or on multiple lines, using the \[lq]format\[rq] subdirective.
-In this case the commodity symbol appears twice and should be the same
-in both places:
-.IP
-.nf
-\f[C]
-;\ commodity\ SYMBOL
-;\ \ \ format\ EXAMPLEAMOUNT
-
-;\ display\ indian\ rupees\ with\ currency\ name\ on\ the\ left,
-;\ thousands,\ lakhs\ and\ crores\ comma\-separated,
-;\ period\ as\ decimal\ point,\ and\ two\ decimal\ places.
-commodity\ INR
-\ \ format\ INR\ 9,99,99,999.00
-\f[]
-.fi
-.SS Default commodity
-.PP
-The D directive sets a default commodity (and display format), to be
-used for amounts without a commodity symbol (ie, plain numbers).
-(Note this differs from Ledger's default commodity directive.) The
-commodity and display format will be applied to all subsequent
-commodity\-less amounts, or until the next D directive.
-.IP
-.nf
-\f[C]
-#\ commodity\-less\ amounts\ should\ be\ treated\ as\ dollars
-#\ (and\ displayed\ with\ symbol\ on\ the\ left,\ thousands\ separators\ and\ two\ decimal\ places)
-D\ $1,000.00
-
-1/1
-\ \ a\ \ \ \ \ 5\ \ \ \ ;\ <\-\ commodity\-less\ amount,\ becomes\ $1
-\ \ b
-\f[]
-.fi
-.SS Default year
-.PP
-You can set a default year to be used for subsequent dates which don't
-specify a year.
-This is a line beginning with \f[C]Y\f[] followed by the year.
-Eg:
-.IP
-.nf
-\f[C]
-Y2009\ \ \ \ \ \ ;\ set\ default\ year\ to\ 2009
-
-12/15\ \ \ \ \ \ ;\ equivalent\ to\ 2009/12/15
-\ \ expenses\ \ 1
-\ \ assets
-
-Y2010\ \ \ \ \ \ ;\ change\ default\ year\ to\ 2010
-
-2009/1/30\ \ ;\ specifies\ the\ year,\ not\ affected
-\ \ expenses\ \ 1
-\ \ assets
-
-1/31\ \ \ \ \ \ \ ;\ equivalent\ to\ 2010/1/31
-\ \ expenses\ \ 1
-\ \ assets
-\f[]
-.fi
-.SS Including other files
-.PP
-You can pull in the content of additional journal files by writing an
-include directive, like this:
-.IP
-.nf
-\f[C]
-include\ path/to/file.journal
-\f[]
-.fi
-.PP
-If the path does not begin with a slash, it is relative to the current
-file.
-Glob patterns (\f[C]*\f[]) are not currently supported.
-.PP
-The \f[C]include\f[] directive can only be used in journal files.
-It can include journal, timeclock or timedot files, but not CSV files.
-.SH Periodic transactions
+.SS Periodic transactions
 .PP
-Periodic transactions are a kind of rule with a dual purpose: they can
-specify recurring future transactions (with \f[C]\-\-forecast\f[]), or
-budget goals (with \f[C]\-\-budget\f[]).
-They look a bit like a transaction, except the first line is a tilde
-(\f[C]~\f[]) followed by a period expression:
+Periodic transaction rules (enabled by \f[C]\-\-forecast\f[] or
+\f[C]\-\-budget\f[]) describe recurring transactions.
+They look like a transaction where the first line is a tilde
+(\f[C]~\f[]) followed by a period expression (mnemonic: \f[C]~\f[] is
+like a recurring sine wave):
 .IP
 .nf
 \f[C]
@@ -1173,25 +1181,34 @@
 \f[]
 .fi
 .PP
+Periodic transactions have a dual purpose:
+.IP \[bu] 2
 With \f[C]\-\-forecast\f[], each periodic transaction rule generates
-recurring \[lq]forecast\[rq] transactions at the specified interval,
-beginning the day after the latest recorded journal transaction (or
-today, if there are no transactions) and ending 6 months from today (or
-at the report end date, if specified).
-.PP
-With \f[C]balance\ \-\-budget\f[], each periodic transaction declares
-recurring budget goals for the specified accounts.
+future transactions, recurring at the specified interval, which can be
+seen in reports.
+Forecast transactions begin the day after the latest recorded journal
+transaction (or today, if there are no transactions) and end 6 months
+from today (or at the report end date, if specified).
+.IP \[bu] 2
+With \f[C]\-\-budget\f[] (supported by the balance command), each
+periodic transaction rule declares recurring budget goals for the
+specified accounts, which can be seen in budget reports.
 Eg the example above declares the goal of receiving $400 from
 \f[C]income:acme\ inc\f[] (and also, depositing $400 into
 \f[C]assets:bank:checking\f[]) every week.
 .PP
-For more details, see: balance: Budgeting and Budgeting and Forecasting.
-.SH Automated postings
+(Actually, you can generate one\-off transactions too, by writing a
+period expression with no report interval.)
 .PP
-Automated postings are postings added automatically by rule to certain
-transactions (with \f[C]\-\-auto\f[]).
+For more details, see: balance: Budget report and Cookbook: Budgeting
+and Forecasting.
+.SS Automated postings
+.PP
+Automated postings (enabled by \f[C]\-\-auto\f[]) are postings added
+automatically by rule to certain transactions.
 An automated posting rule looks like a transaction where the first line
-is an equal sign (\f[C]=\f[]) followed by a query:
+is an equal sign (\f[C]=\f[]) followed by a query (mnemonic: \f[C]=\f[]
+tests for matching transactions, and also looks like posting lines):
 .IP
 .nf
 \f[C]
@@ -1226,10 +1243,13 @@
 $\ hledger\ print\ \-\-auto
 2017/12/14
 \ \ \ \ expenses:gifts\ \ \ \ \ \ \ \ \ \ \ \ \ $20
-\ \ \ \ assets
 \ \ \ \ (budget:gifts)\ \ \ \ \ \ \ \ \ \ \ \ $\-20
+\ \ \ \ assets
 \f[]
 .fi
+.PP
+Like postings recorded by hand, automated postings participate in
+transaction balancing, missing amount inference and balance assertions.
 .SH EDITOR SUPPORT
 .PP
 Add\-on modes exist for various text editors, to make working with
diff --git a/hledger_journal.info b/hledger_journal.info
--- a/hledger_journal.info
+++ b/hledger_journal.info
@@ -4,8 +4,8 @@
 
 File: hledger_journal.info,  Node: Top,  Next: FILE FORMAT,  Up: (dir)
 
-hledger_journal(5) hledger 1.9
-******************************
+hledger_journal(5) hledger 1.9.1
+********************************
 
 hledger's usual data source is a plain text file containing journal
 entries in hledger journal format.  This file represents a standard
@@ -57,12 +57,10 @@
 * Menu:
 
 * FILE FORMAT::
-* Periodic transactions::
-* Automated postings::
 * EDITOR SUPPORT::
 
 
-File: hledger_journal.info,  Node: FILE FORMAT,  Next: Periodic transactions,  Prev: Top,  Up: Top
+File: hledger_journal.info,  Node: FILE FORMAT,  Next: EDITOR SUPPORT,  Prev: Top,  Up: Top
 
 1 FILE FORMAT
 *************
@@ -83,6 +81,8 @@
 * Comments::
 * Tags::
 * Directives::
+* Periodic transactions::
+* Automated postings::
 
 
 File: hledger_journal.info,  Node: Transactions,  Next: Postings,  Up: FILE FORMAT
@@ -713,10 +713,6 @@
 org-mode nodes to be ignored, allowing emacs users to fold and navigate
 their journals with org-mode or orgstruct-mode.)
 
-   Also, anything between 'comment' and 'end comment' directives is a
-(multi-line) comment.  If there is no 'end comment', the comment extends
-to the end of the file.
-
    You can attach comments to a transaction by writing them after the
 description and/or indented on the following lines (before the
 postings).  Similarly, you can attach comments to an individual posting
@@ -744,6 +740,9 @@
     ; another comment line for posting 2
 ; a file comment (because not indented)
 
+   You can also comment larger regions of a file using 'comment' and
+'end comment' directives.
+
 
 File: hledger_journal.info,  Node: Tags,  Next: Directives,  Prev: Comments,  Up: FILE FORMAT
 
@@ -788,28 +787,184 @@
 are simple strings.
 
 
-File: hledger_journal.info,  Node: Directives,  Prev: Tags,  Up: FILE FORMAT
+File: hledger_journal.info,  Node: Directives,  Next: Periodic transactions,  Prev: Tags,  Up: FILE FORMAT
 
 1.14 Directives
 ===============
 
 * Menu:
 
-* Account aliases::
-* account directive::
-* apply account directive::
-* Multi-line comments::
-* commodity directive::
-* Default commodity::
-* Default year::
+* Comment blocks::
 * Including other files::
+* Default year::
+* Declaring commodities::
+* Default commodity::
+* Declaring accounts::
+* Rewriting accounts::
+* Default parent account::
 
 
-File: hledger_journal.info,  Node: Account aliases,  Next: account directive,  Up: Directives
+File: hledger_journal.info,  Node: Comment blocks,  Next: Including other files,  Up: Directives
 
-1.14.1 Account aliases
-----------------------
+1.14.1 Comment blocks
+---------------------
 
+A line containing just 'comment' starts a commented region of the file,
+and a line containing just 'end comment' (or the end of the current
+file) ends it.  See also comments.
+
+
+File: hledger_journal.info,  Node: Including other files,  Next: Default year,  Prev: Comment blocks,  Up: Directives
+
+1.14.2 Including other files
+----------------------------
+
+You can pull in the content of additional files by writing an include
+directive, like this:
+
+include path/to/file.journal
+
+   If the path does not begin with a slash, it is relative to the
+current file.  Glob patterns ('*') are not currently supported.
+
+   The 'include' directive can only be used in journal files.  It can
+include journal, timeclock or timedot files, but not CSV files.
+
+
+File: hledger_journal.info,  Node: Default year,  Next: Declaring commodities,  Prev: Including other files,  Up: Directives
+
+1.14.3 Default year
+-------------------
+
+You can set a default year to be used for subsequent dates which don't
+specify a year.  This is a line beginning with 'Y' followed by the year.
+Eg:
+
+Y2009      ; set default year to 2009
+
+12/15      ; equivalent to 2009/12/15
+  expenses  1
+  assets
+
+Y2010      ; change default year to 2010
+
+2009/1/30  ; specifies the year, not affected
+  expenses  1
+  assets
+
+1/31       ; equivalent to 2010/1/31
+  expenses  1
+  assets
+
+
+File: hledger_journal.info,  Node: Declaring commodities,  Next: Default commodity,  Prev: Default year,  Up: Directives
+
+1.14.4 Declaring commodities
+----------------------------
+
+The 'commodity' directive declares commodities which may be used in the
+journal (though currently we do not enforce this).  It may be written on
+a single line, like this:
+
+; commodity EXAMPLEAMOUNT
+
+; display AAAA amounts with the symbol on the right, space-separated,
+; using period as decimal point, with four decimal places, and
+; separating thousands with comma.
+commodity 1,000.0000 AAAA
+
+   or on multiple lines, using the "format" subdirective.  In this case
+the commodity symbol appears twice and should be the same in both
+places:
+
+; commodity SYMBOL
+;   format EXAMPLEAMOUNT
+
+; display indian rupees with currency name on the left,
+; thousands, lakhs and crores comma-separated,
+; period as decimal point, and two decimal places.
+commodity INR
+  format INR 9,99,99,999.00
+
+   Commodity directives have a second purpose: they define the standard
+display format for amounts in the commodity.  Normally the display
+format is inferred from journal entries, but this can be unpredictable;
+declaring it with a commodity directive overrides this and removes
+ambiguity.  Towards this end, amounts in commodity directives must
+always be written with a decimal point (a period or comma, followed by 0
+or more decimal digits).
+
+
+File: hledger_journal.info,  Node: Default commodity,  Next: Declaring accounts,  Prev: Declaring commodities,  Up: Directives
+
+1.14.5 Default commodity
+------------------------
+
+The D directive sets a default commodity (and display format), to be
+used for amounts without a commodity symbol (ie, plain numbers).  (Note
+this differs from Ledger's default commodity directive.)  The commodity
+and display format will be applied to all subsequent commodity-less
+amounts, or until the next D directive.
+
+# commodity-less amounts should be treated as dollars
+# (and displayed with symbol on the left, thousands separators and two decimal places)
+D $1,000.00
+
+1/1
+  a     5    ; <- commodity-less amount, becomes $1
+  b
+
+   As with the 'commodity' directive, the amount must always be written
+with a decimal point.
+
+
+File: hledger_journal.info,  Node: Declaring accounts,  Next: Rewriting accounts,  Prev: Default commodity,  Up: Directives
+
+1.14.6 Declaring accounts
+-------------------------
+
+The 'account' directive predeclares account names.  The simplest form is
+'account ACCTNAME', eg:
+
+account assets:bank:checking
+
+   Currently this mainly helps with account name autocompletion in eg
+hledger add, hledger-iadd, hledger-web, and ledger-mode.
+In future it will also help detect misspelled accounts.
+
+   Account names can be followed by a numeric account code:
+
+account assets                  1000
+account assets:bank:checking    1110
+account liabilities             2000
+account revenues                4000
+account expenses                6000
+
+   This affects account display order in reports: accounts with codes
+are listed before accounts without codes, in increasing code order.
+(Otherwise, accounts are listed alphabetically.)  Account codes should
+be all numeric digits, unique, and separated from the account name by at
+least two spaces (since account names may contain single spaces).  By
+convention, often the first digit indicates the type of account, as in
+this numbering scheme and the example above.  In future, we might use
+this to recognize account types.
+
+   An account directive can also have indented subdirectives following
+it, which are currently ignored.  Here is the full syntax:
+
+; account ACCTNAME  [OPTIONALCODE]
+;   [OPTIONALSUBDIRECTIVES]
+
+account assets:bank:checking   1110
+  a comment
+  some-tag:12345
+
+
+File: hledger_journal.info,  Node: Rewriting accounts,  Next: Default parent account,  Prev: Declaring accounts,  Up: Directives
+
+1.14.7 Rewriting accounts
+-------------------------
+
 You can define aliases which rewrite your account names (after reading
 the journal, before generating reports).  hledger's account aliases can
 be useful for:
@@ -821,7 +976,7 @@
      or combining two accounts into one
    * customising reports
 
-   See also Cookbook: rewrite account names.
+   See also Cookbook: Rewrite account names.
 * Menu:
 
 * Basic aliases::
@@ -830,9 +985,9 @@
 * end aliases::
 
 
-File: hledger_journal.info,  Node: Basic aliases,  Next: Regex aliases,  Up: Account aliases
+File: hledger_journal.info,  Node: Basic aliases,  Next: Regex aliases,  Up: Rewriting accounts
 
-1.14.1.1 Basic aliases
+1.14.7.1 Basic aliases
 ......................
 
 To set an account alias, use the 'alias' directive in your journal file.
@@ -853,9 +1008,9 @@
 # rewrites "checking" to "assets:bank:wells fargo:checking", or "checking:a" to "assets:bank:wells fargo:checking:a"
 
 
-File: hledger_journal.info,  Node: Regex aliases,  Next: Multiple aliases,  Prev: Basic aliases,  Up: Account aliases
+File: hledger_journal.info,  Node: Regex aliases,  Next: Multiple aliases,  Prev: Basic aliases,  Up: Rewriting accounts
 
-1.14.1.2 Regex aliases
+1.14.7.2 Regex aliases
 ......................
 
 There is also a more powerful variant that uses a regular expression,
@@ -878,9 +1033,9 @@
 whitespace.
 
 
-File: hledger_journal.info,  Node: Multiple aliases,  Next: end aliases,  Prev: Regex aliases,  Up: Account aliases
+File: hledger_journal.info,  Node: Multiple aliases,  Next: end aliases,  Prev: Regex aliases,  Up: Rewriting accounts
 
-1.14.1.3 Multiple aliases
+1.14.7.3 Multiple aliases
 .........................
 
 You can define as many aliases as you like using directives or
@@ -894,10 +1049,10 @@
   2. alias options, in the order they appear on the command line
 
 
-File: hledger_journal.info,  Node: end aliases,  Prev: Multiple aliases,  Up: Account aliases
+File: hledger_journal.info,  Node: end aliases,  Prev: Multiple aliases,  Up: Rewriting accounts
 
-1.14.1.4 end aliases
-....................
+1.14.7.4 'end aliases'
+......................
 
 You can clear (forget) all currently defined aliases with the 'end
 aliases' directive:
@@ -905,52 +1060,10 @@
 end aliases
 
 
-File: hledger_journal.info,  Node: account directive,  Next: apply account directive,  Prev: Account aliases,  Up: Directives
-
-1.14.2 account directive
-------------------------
-
-The 'account' directive predeclares account names.  The simplest form is
-'account ACCTNAME', eg:
-
-account assets:bank:checking
-
-   Currently this mainly helps with account name autocompletion in eg
-hledger add, hledger-iadd, hledger-web, and ledger-mode.
-In future it will also help detect misspelled accounts.
-
-   Account names can be followed by a numeric account code:
-
-account assets                  1000
-account assets:bank:checking    1110
-account liabilities             2000
-account revenues                4000
-account expenses                6000
-
-   This affects account display order in reports: accounts with codes
-are listed before accounts without codes, in increasing code order.
-(Otherwise, accounts are listed alphabetically.)  Account codes should
-be all numeric digits, unique, and separated from the account name by at
-least two spaces (since account names may contain single spaces).  By
-convention, often the first digit indicates the type of account, as in
-this numbering scheme and the example above.  In future, we might use
-this to recognize account types.
-
-   An account directive can also have indented subdirectives following
-it, which are currently ignored.  Here is the full syntax:
-
-; account ACCTNAME  [OPTIONALCODE]
-;   [OPTIONALSUBDIRECTIVES]
-
-account assets:bank:checking   1110
-  a comment
-  some-tag:12345
-
-
-File: hledger_journal.info,  Node: apply account directive,  Next: Multi-line comments,  Prev: account directive,  Up: Directives
+File: hledger_journal.info,  Node: Default parent account,  Prev: Rewriting accounts,  Up: Directives
 
-1.14.3 apply account directive
-------------------------------
+1.14.8 Default parent account
+-----------------------------
 
 You can specify a parent account which will be prepended to all accounts
 within a section of the journal.  Use the 'apply account' and 'end apply
@@ -983,148 +1096,52 @@
 supported.
 
 
-File: hledger_journal.info,  Node: Multi-line comments,  Next: commodity directive,  Prev: apply account directive,  Up: Directives
-
-1.14.4 Multi-line comments
---------------------------
-
-A line containing just 'comment' starts a multi-line comment, and a line
-containing just 'end comment' ends it.  See comments.
-
-
-File: hledger_journal.info,  Node: commodity directive,  Next: Default commodity,  Prev: Multi-line comments,  Up: Directives
-
-1.14.5 commodity directive
---------------------------
-
-The 'commodity' directive predefines commodities (currently this is just
-informational), and also it may define the display format for amounts in
-this commodity (overriding the automatically inferred format).
-
-   It may be written on a single line, like this:
-
-; commodity EXAMPLEAMOUNT
-
-; display AAAA amounts with the symbol on the right, space-separated,
-; using period as decimal point, with four decimal places, and
-; separating thousands with comma.
-commodity 1,000.0000 AAAA
-
-   or on multiple lines, using the "format" subdirective.  In this case
-the commodity symbol appears twice and should be the same in both
-places:
-
-; commodity SYMBOL
-;   format EXAMPLEAMOUNT
-
-; display indian rupees with currency name on the left,
-; thousands, lakhs and crores comma-separated,
-; period as decimal point, and two decimal places.
-commodity INR
-  format INR 9,99,99,999.00
-
-
-File: hledger_journal.info,  Node: Default commodity,  Next: Default year,  Prev: commodity directive,  Up: Directives
-
-1.14.6 Default commodity
-------------------------
-
-The D directive sets a default commodity (and display format), to be
-used for amounts without a commodity symbol (ie, plain numbers).  (Note
-this differs from Ledger's default commodity directive.)  The commodity
-and display format will be applied to all subsequent commodity-less
-amounts, or until the next D directive.
-
-# commodity-less amounts should be treated as dollars
-# (and displayed with symbol on the left, thousands separators and two decimal places)
-D $1,000.00
-
-1/1
-  a     5    ; <- commodity-less amount, becomes $1
-  b
-
-
-File: hledger_journal.info,  Node: Default year,  Next: Including other files,  Prev: Default commodity,  Up: Directives
-
-1.14.7 Default year
--------------------
-
-You can set a default year to be used for subsequent dates which don't
-specify a year.  This is a line beginning with 'Y' followed by the year.
-Eg:
-
-Y2009      ; set default year to 2009
-
-12/15      ; equivalent to 2009/12/15
-  expenses  1
-  assets
-
-Y2010      ; change default year to 2010
-
-2009/1/30  ; specifies the year, not affected
-  expenses  1
-  assets
-
-1/31       ; equivalent to 2010/1/31
-  expenses  1
-  assets
-
-
-File: hledger_journal.info,  Node: Including other files,  Prev: Default year,  Up: Directives
-
-1.14.8 Including other files
-----------------------------
-
-You can pull in the content of additional journal files by writing an
-include directive, like this:
-
-include path/to/file.journal
-
-   If the path does not begin with a slash, it is relative to the
-current file.  Glob patterns ('*') are not currently supported.
-
-   The 'include' directive can only be used in journal files.  It can
-include journal, timeclock or timedot files, but not CSV files.
-
-
-File: hledger_journal.info,  Node: Periodic transactions,  Next: Automated postings,  Prev: FILE FORMAT,  Up: Top
+File: hledger_journal.info,  Node: Periodic transactions,  Next: Automated postings,  Prev: Directives,  Up: FILE FORMAT
 
-2 Periodic transactions
-***********************
+1.15 Periodic transactions
+==========================
 
-Periodic transactions are a kind of rule with a dual purpose: they can
-specify recurring future transactions (with '--forecast'), or budget
-goals (with '--budget').  They look a bit like a transaction, except the
-first line is a tilde ('~') followed by a period expression:
+Periodic transaction rules (enabled by '--forecast' or '--budget')
+describe recurring transactions.  They look like a transaction where the
+first line is a tilde ('~') followed by a period expression (mnemonic:
+'~' is like a recurring sine wave):
 
 ~ weekly
   assets:bank:checking   $400 ; paycheck
   income:acme inc
 
-   With '--forecast', each periodic transaction rule generates recurring
-"forecast" transactions at the specified interval, beginning the day
-after the latest recorded journal transaction (or today, if there are no
-transactions) and ending 6 months from today (or at the report end date,
-if specified).
+   Periodic transactions have a dual purpose:
 
-   With 'balance --budget', each periodic transaction declares recurring
-budget goals for the specified accounts.  Eg the example above declares
-the goal of receiving $400 from 'income:acme inc' (and also, depositing
-$400 into 'assets:bank:checking') every week.
+   * With '--forecast', each periodic transaction rule generates future
+     transactions, recurring at the specified interval, which can be
+     seen in reports.  Forecast transactions begin the day after the
+     latest recorded journal transaction (or today, if there are no
+     transactions) and end 6 months from today (or at the report end
+     date, if specified).
 
-   For more details, see: balance: Budgeting and Budgeting and
-Forecasting.
+   * With '--budget' (supported by the balance command), each periodic
+     transaction rule declares recurring budget goals for the specified
+     accounts, which can be seen in budget reports.  Eg the example
+     above declares the goal of receiving $400 from 'income:acme inc'
+     (and also, depositing $400 into 'assets:bank:checking') every week.
 
+   (Actually, you can generate one-off transactions too, by writing a
+period expression with no report interval.)
+
+   For more details, see: balance: Budget report and Cookbook: Budgeting
+and Forecasting.
+
 
-File: hledger_journal.info,  Node: Automated postings,  Next: EDITOR SUPPORT,  Prev: Periodic transactions,  Up: Top
+File: hledger_journal.info,  Node: Automated postings,  Prev: Periodic transactions,  Up: FILE FORMAT
 
-3 Automated postings
-********************
+1.16 Automated postings
+=======================
 
-Automated postings are postings added automatically by rule to certain
-transactions (with '--auto').  An automated posting rule looks like a
-transaction where the first line is an equal sign ('=') followed by a
-query:
+Automated postings (enabled by '--auto') are postings added
+automatically by rule to certain transactions.  An automated posting
+rule looks like a transaction where the first line is an equal sign
+('=') followed by a query (mnemonic: '=' tests for matching
+transactions, and also looks like posting lines):
 
 = expenses:gifts
     budget:gifts  *-1
@@ -1149,13 +1166,16 @@
 $ hledger print --auto
 2017/12/14
     expenses:gifts             $20
-    assets
     (budget:gifts)            $-20
+    assets
 
+   Like postings recorded by hand, automated postings participate in
+transaction balancing, missing amount inference and balance assertions.
+
 
-File: hledger_journal.info,  Node: EDITOR SUPPORT,  Prev: Automated postings,  Up: Top
+File: hledger_journal.info,  Node: EDITOR SUPPORT,  Prev: FILE FORMAT,  Up: Top
 
-4 EDITOR SUPPORT
+2 EDITOR SUPPORT
 ****************
 
 Add-on modes exist for various text editors, to make working with
@@ -1182,89 +1202,89 @@
 
 Tag Table:
 Node: Top76
-Node: FILE FORMAT2419
-Ref: #file-format2550
-Node: Transactions2773
-Ref: #transactions2894
-Node: Postings3578
-Ref: #postings3705
-Node: Dates4700
-Ref: #dates4815
-Node: Simple dates4880
-Ref: #simple-dates5006
-Node: Secondary dates5372
-Ref: #secondary-dates5526
-Node: Posting dates7089
-Ref: #posting-dates7218
-Node: Status8592
-Ref: #status8712
-Node: Description10420
-Ref: #description10558
-Node: Payee and note10877
-Ref: #payee-and-note10991
-Node: Account names11233
-Ref: #account-names11376
-Node: Amounts11863
-Ref: #amounts11999
-Node: Virtual Postings15014
-Ref: #virtual-postings15173
-Node: Balance Assertions16393
-Ref: #balance-assertions16568
-Node: Assertions and ordering17464
-Ref: #assertions-and-ordering17650
-Node: Assertions and included files18350
-Ref: #assertions-and-included-files18591
-Node: Assertions and multiple -f options18924
-Ref: #assertions-and-multiple--f-options19178
-Node: Assertions and commodities19310
-Ref: #assertions-and-commodities19545
-Node: Assertions and subaccounts20241
-Ref: #assertions-and-subaccounts20473
-Node: Assertions and virtual postings20994
-Ref: #assertions-and-virtual-postings21201
-Node: Balance Assignments21343
-Ref: #balance-assignments21512
-Node: Prices22632
-Ref: #prices22765
-Node: Transaction prices22816
-Ref: #transaction-prices22961
-Node: Market prices25117
-Ref: #market-prices25252
-Node: Comments26212
-Ref: #comments26334
-Node: Tags27576
-Ref: #tags27694
-Node: Directives29096
-Ref: #directives29209
-Node: Account aliases29402
-Ref: #account-aliases29546
-Node: Basic aliases30150
-Ref: #basic-aliases30293
-Node: Regex aliases30983
-Ref: #regex-aliases31151
-Node: Multiple aliases31869
-Ref: #multiple-aliases32041
-Node: end aliases32539
-Ref: #end-aliases32679
-Node: account directive32780
-Ref: #account-directive32960
-Node: apply account directive34307
-Ref: #apply-account-directive34503
-Node: Multi-line comments35162
-Ref: #multi-line-comments35352
-Node: commodity directive35480
-Ref: #commodity-directive35664
-Node: Default commodity36536
-Ref: #default-commodity36709
-Node: Default year37246
-Ref: #default-year37411
-Node: Including other files37834
-Ref: #including-other-files37991
-Node: Periodic transactions38388
-Ref: #periodic-transactions38554
-Node: Automated postings39543
-Ref: #automated-postings39706
-Node: EDITOR SUPPORT40608
-Ref: #editor-support40733
+Node: FILE FORMAT2374
+Ref: #file-format2498
+Node: Transactions2770
+Ref: #transactions2891
+Node: Postings3575
+Ref: #postings3702
+Node: Dates4697
+Ref: #dates4812
+Node: Simple dates4877
+Ref: #simple-dates5003
+Node: Secondary dates5369
+Ref: #secondary-dates5523
+Node: Posting dates7086
+Ref: #posting-dates7215
+Node: Status8589
+Ref: #status8709
+Node: Description10417
+Ref: #description10555
+Node: Payee and note10874
+Ref: #payee-and-note10988
+Node: Account names11230
+Ref: #account-names11373
+Node: Amounts11860
+Ref: #amounts11996
+Node: Virtual Postings15011
+Ref: #virtual-postings15170
+Node: Balance Assertions16390
+Ref: #balance-assertions16565
+Node: Assertions and ordering17461
+Ref: #assertions-and-ordering17647
+Node: Assertions and included files18347
+Ref: #assertions-and-included-files18588
+Node: Assertions and multiple -f options18921
+Ref: #assertions-and-multiple--f-options19175
+Node: Assertions and commodities19307
+Ref: #assertions-and-commodities19542
+Node: Assertions and subaccounts20238
+Ref: #assertions-and-subaccounts20470
+Node: Assertions and virtual postings20991
+Ref: #assertions-and-virtual-postings21198
+Node: Balance Assignments21340
+Ref: #balance-assignments21509
+Node: Prices22629
+Ref: #prices22762
+Node: Transaction prices22813
+Ref: #transaction-prices22958
+Node: Market prices25114
+Ref: #market-prices25249
+Node: Comments26209
+Ref: #comments26331
+Node: Tags27501
+Ref: #tags27619
+Node: Directives29021
+Ref: #directives29164
+Node: Comment blocks29357
+Ref: #comment-blocks29502
+Node: Including other files29678
+Ref: #including-other-files29858
+Node: Default year30247
+Ref: #default-year30416
+Node: Declaring commodities30839
+Ref: #declaring-commodities31022
+Node: Default commodity32249
+Ref: #default-commodity32430
+Node: Declaring accounts33062
+Ref: #declaring-accounts33242
+Node: Rewriting accounts34589
+Ref: #rewriting-accounts34774
+Node: Basic aliases35378
+Ref: #basic-aliases35524
+Node: Regex aliases36214
+Ref: #regex-aliases36385
+Node: Multiple aliases37103
+Ref: #multiple-aliases37278
+Node: end aliases37776
+Ref: #end-aliases37923
+Node: Default parent account38024
+Ref: #default-parent-account38190
+Node: Periodic transactions38849
+Ref: #periodic-transactions39028
+Node: Automated postings40327
+Ref: #automated-postings40481
+Node: EDITOR SUPPORT41614
+Ref: #editor-support41732
 
 End Tag Table
diff --git a/hledger_journal.txt b/hledger_journal.txt
--- a/hledger_journal.txt
+++ b/hledger_journal.txt
@@ -553,10 +553,6 @@
        nodes to be ignored, allowing emacs users to fold  and  navigate  their
        journals with org-mode or orgstruct-mode.)
 
-       Also,   anything  between  comment  and  end comment  directives  is  a
-       (multi-line) comment.  If there is no end comment, the comment  extends
-       to the end of the file.
-
        You  can  attach  comments  to  a transaction by writing them after the
        description and/or indented on the following lines  (before  the  post-
        ings).   Similarly, you can attach comments to an individual posting by
@@ -584,21 +580,24 @@
                   ; another comment line for posting 2
               ; a file comment (because not indented)
 
+       You  can  also  comment  larger  regions  of  a  file using comment and
+       end comment directives.
+
    Tags
-       Tags  are  a  way  to add extra labels or labelled data to postings and
+       Tags are a way to add extra labels or labelled  data  to  postings  and
        transactions, which you can then search or pivot on.
 
-       A simple tag is a word (which may contain hyphens) followed by  a  full
+       A  simple  tag is a word (which may contain hyphens) followed by a full
        colon, written inside a transaction or posting comment line:
 
               2017/1/16 bought groceries    ; sometag:
 
-       Tags  can  have  a  value, which is the text after the colon, up to the
+       Tags can have a value, which is the text after the  colon,  up  to  the
        next comma or end of line, with leading/trailing whitespace removed:
 
                   expenses:food    $10   ; a-posting-tag: the tag value
 
-       Note this means hledger's tag values can not  contain  commas  or  new-
+       Note  this  means  hledger's  tag values can not contain commas or new-
        lines.  Ending at commas means you can write multiple short tags on one
        line, comma separated:
 
@@ -612,21 +611,147 @@
 
        o "tag2" is another tag, whose value is "some value ..."
 
-       Tags in a transaction comment affect the transaction  and  all  of  its
-       postings,  while  tags  in  a posting comment affect only that posting.
-       For example,  the  following  transaction  has  three  tags  (A,  TAG2,
+       Tags  in  a  transaction  comment affect the transaction and all of its
+       postings, while tags in a posting comment  affect  only  that  posting.
+       For  example,  the  following  transaction  has  three  tags  (A, TAG2,
        third-tag) and the posting has four (those plus posting-tag):
 
               1/1 a transaction  ; A:, TAG2:
                   ; third-tag: a third transaction tag, <- with a value
                   (a)  $1  ; posting-tag:
 
-       Tags  are  like  Ledger's metadata feature, except hledger's tag values
+       Tags are like Ledger's metadata feature, except  hledger's  tag  values
        are simple strings.
 
    Directives
-   Account aliases
-       You can define aliases which rewrite your account names (after  reading
+   Comment blocks
+       A  line  containing just comment starts a commented region of the file,
+       and a line containing just end comment (or the end of the current file)
+       ends it.  See also comments.
+
+   Including other files
+       You  can  pull in the content of additional files by writing an include
+       directive, like this:
+
+              include path/to/file.journal
+
+       If the path does not begin with a slash, it is relative to the  current
+       file.  Glob patterns (*) are not currently supported.
+
+       The  include  directive  can  only  be  used  in journal files.  It can
+       include journal, timeclock or timedot files, but not CSV files.
+
+   Default year
+       You can set a default year to be used for subsequent dates which  don't
+       specify  a year.  This is a line beginning with Y followed by the year.
+       Eg:
+
+              Y2009      ; set default year to 2009
+
+              12/15      ; equivalent to 2009/12/15
+                expenses  1
+                assets
+
+              Y2010      ; change default year to 2010
+
+              2009/1/30  ; specifies the year, not affected
+                expenses  1
+                assets
+
+              1/31       ; equivalent to 2010/1/31
+                expenses  1
+                assets
+
+   Declaring commodities
+       The commodity directive declares commodities which may be used  in  the
+       journal  (though  currently we do not enforce this).  It may be written
+       on a single line, like this:
+
+              ; commodity EXAMPLEAMOUNT
+
+              ; display AAAA amounts with the symbol on the right, space-separated,
+              ; using period as decimal point, with four decimal places, and
+              ; separating thousands with comma.
+              commodity 1,000.0000 AAAA
+
+       or on multiple lines, using the "format" subdirective.   In  this  case
+       the  commodity  symbol  appears  twice  and  should be the same in both
+       places:
+
+              ; commodity SYMBOL
+              ;   format EXAMPLEAMOUNT
+
+              ; display indian rupees with currency name on the left,
+              ; thousands, lakhs and crores comma-separated,
+              ; period as decimal point, and two decimal places.
+              commodity INR
+                format INR 9,99,99,999.00
+
+       Commodity directives have a second purpose: they  define  the  standard
+       display format for amounts in the commodity.  Normally the display for-
+       mat is inferred from journal entries, but this  can  be  unpredictable;
+       declaring  it  with  a  commodity  directive overrides this and removes
+       ambiguity.  Towards this end,  amounts  in  commodity  directives  must
+       always  be written with a decimal point (a period or comma, followed by
+       0 or more decimal digits).
+
+   Default commodity
+       The D directive sets a default commodity (and display  format),  to  be
+       used for amounts without a commodity symbol (ie, plain numbers).  (Note
+       this differs from Ledger's default commodity directive.) The  commodity
+       and  display  format  will  be applied to all subsequent commodity-less
+       amounts, or until the next D directive.
+
+              # commodity-less amounts should be treated as dollars
+              # (and displayed with symbol on the left, thousands separators and two decimal places)
+              D $1,000.00
+
+              1/1
+                a     5    ; <- commodity-less amount, becomes $1
+                b
+
+       As with the commodity directive, the amount must always be written with
+       a decimal point.
+
+   Declaring accounts
+       The  account directive predeclares account names.  The simplest form is
+       account ACCTNAME, eg:
+
+              account assets:bank:checking
+
+       Currently this mainly helps with  account  name  autocompletion  in  eg
+       hledger add, hledger-iadd, hledger-web, and ledger-mode.
+       In future it will also help detect misspelled accounts.
+
+       Account names can be followed by a numeric account code:
+
+              account assets                  1000
+              account assets:bank:checking    1110
+              account liabilities             2000
+              account revenues                4000
+              account expenses                6000
+
+       This  affects account display order in reports: accounts with codes are
+       listed before accounts without codes, in increasing code order.   (Oth-
+       erwise,  accounts  are  listed alphabetically.) Account codes should be
+       all numeric digits, unique, and separated from the account name  by  at
+       least  two  spaces (since account names may contain single spaces).  By
+       convention, often the first digit indicates the type of account, as  in
+       this  numbering  scheme and the example above.  In future, we might use
+       this to recognize account types.
+
+       An account directive can also have indented subdirectives following it,
+       which are currently ignored.  Here is the full syntax:
+
+              ; account ACCTNAME  [OPTIONALCODE]
+              ;   [OPTIONALSUBDIRECTIVES]
+
+              account assets:bank:checking   1110
+                a comment
+                some-tag:12345
+
+   Rewriting accounts
+       You  can define aliases which rewrite your account names (after reading
        the journal, before generating reports).  hledger's account aliases can
        be useful for:
 
@@ -640,11 +765,11 @@
 
        o customising reports
 
-       See also Cookbook: rewrite account names.
+       See also Cookbook: Rewrite account names.
 
    Basic aliases
-       To set an account alias, use the alias directive in your journal  file.
-       This  affects all subsequent journal entries in the current file or its
+       To  set an account alias, use the alias directive in your journal file.
+       This affects all subsequent journal entries in the current file or  its
        included files.  The spaces around the = are optional:
 
               alias OLD = NEW
@@ -652,91 +777,54 @@
        Or, you can use the --alias 'OLD=NEW' option on the command line.  This
        affects all entries.  It's useful for trying out aliases interactively.
 
-       OLD and NEW are full account names.  hledger will  replace  any  occur-
-       rence  of  the old account name with the new one.  Subaccounts are also
+       OLD  and  NEW  are full account names.  hledger will replace any occur-
+       rence of the old account name with the new one.  Subaccounts  are  also
        affected.  Eg:
 
               alias checking = assets:bank:wells fargo:checking
               # rewrites "checking" to "assets:bank:wells fargo:checking", or "checking:a" to "assets:bank:wells fargo:checking:a"
 
    Regex aliases
-       There is also a more powerful variant that uses a  regular  expression,
+       There  is  also a more powerful variant that uses a regular expression,
        indicated by the forward slashes:
 
               alias /REGEX/ = REPLACEMENT
 
        or --alias '/REGEX/=REPLACEMENT'.
 
-       REGEX  is  a  case-insensitive regular expression.  Anywhere it matches
-       inside an account name, the matched part will be replaced  by  REPLACE-
-       MENT.   If REGEX contains parenthesised match groups, these can be ref-
+       REGEX is a case-insensitive regular expression.   Anywhere  it  matches
+       inside  an  account name, the matched part will be replaced by REPLACE-
+       MENT.  If REGEX contains parenthesised match groups, these can be  ref-
        erenced by the usual numeric backreferences in REPLACEMENT.  Eg:
 
               alias /^(.+):bank:([^:]+)(.*)/ = \1:\2 \3
               # rewrites "assets:bank:wells fargo:checking" to  "assets:wells fargo checking"
 
-       Also note that REPLACEMENT continues to the end of line (or on  command
-       line,  to  end  of  option argument), so it can contain trailing white-
+       Also  note that REPLACEMENT continues to the end of line (or on command
+       line, to end of option argument), so it  can  contain  trailing  white-
        space.
 
    Multiple aliases
-       You can define as many aliases as you like  using  directives  or  com-
-       mand-line  options.  Aliases are recursive - each alias sees the result
-       of applying previous ones.   (This  is  different  from  Ledger,  where
+       You  can  define  as  many aliases as you like using directives or com-
+       mand-line options.  Aliases are recursive - each alias sees the  result
+       of  applying  previous  ones.   (This  is  different from Ledger, where
        aliases are non-recursive by default).  Aliases are applied in the fol-
        lowing order:
 
-       1. alias directives, most recently seen first (recent  directives  take
+       1. alias  directives,  most recently seen first (recent directives take
           precedence over earlier ones; directives not yet seen are ignored)
 
        2. alias options, in the order they appear on the command line
 
    end aliases
-       You   can  clear  (forget)  all  currently  defined  aliases  with  the
+       You  can  clear  (forget)  all  currently  defined  aliases  with   the
        end aliases directive:
 
               end aliases
 
-   account directive
-       The account directive predeclares account names.  The simplest form  is
-       account ACCTNAME, eg:
-
-              account assets:bank:checking
-
-       Currently  this  mainly  helps  with  account name autocompletion in eg
-       hledger add, hledger-iadd, hledger-web, and ledger-mode.
-       In future it will also help detect misspelled accounts.
-
-       Account names can be followed by a numeric account code:
-
-              account assets                  1000
-              account assets:bank:checking    1110
-              account liabilities             2000
-              account revenues                4000
-              account expenses                6000
-
-       This affects account display order in reports: accounts with codes  are
-       listed  before accounts without codes, in increasing code order.  (Oth-
-       erwise, accounts are listed alphabetically.) Account  codes  should  be
-       all  numeric  digits, unique, and separated from the account name by at
-       least two spaces (since account names may contain single  spaces).   By
-       convention,  often the first digit indicates the type of account, as in
-       this numbering scheme and the example above.  In future, we  might  use
-       this to recognize account types.
-
-       An account directive can also have indented subdirectives following it,
-       which are currently ignored.  Here is the full syntax:
-
-              ; account ACCTNAME  [OPTIONALCODE]
-              ;   [OPTIONALSUBDIRECTIVES]
-
-              account assets:bank:checking   1110
-                a comment
-                some-tag:12345
-
-   apply account directive
-       You can specify a  parent  account  which  will  be  prepended  to  all
-       accounts  within  a  section of the journal.  Use the apply account and
+   Default parent account
+       You  can  specify  a  parent  account  which  will  be prepended to all
+       accounts within a section of the journal.  Use  the  apply account  and
        end apply account directives like so:
 
               apply account home
@@ -753,7 +841,7 @@
                   home:food           $10
                   home:cash          $-10
 
-       If end apply account is omitted, the effect lasts to  the  end  of  the
+       If  end apply account  is  omitted,  the effect lasts to the end of the
        file.  Included files are also affected, eg:
 
               apply account business
@@ -762,129 +850,58 @@
               apply account personal
               include personal.journal
 
-       Prior  to  hledger 1.0, legacy account and end spellings were also sup-
+       Prior to hledger 1.0, legacy account and end spellings were  also  sup-
        ported.
 
-   Multi-line comments
-       A line containing just comment starts a multi-line comment, and a  line
-       containing just end comment ends it.  See comments.
-
-   commodity directive
-       The  commodity directive predefines commodities (currently this is just
-       informational), and also it may define the display format  for  amounts
-       in this commodity (overriding the automatically inferred format).
-
-       It may be written on a single line, like this:
-
-              ; commodity EXAMPLEAMOUNT
-
-              ; display AAAA amounts with the symbol on the right, space-separated,
-              ; using period as decimal point, with four decimal places, and
-              ; separating thousands with comma.
-              commodity 1,000.0000 AAAA
-
-       or  on  multiple  lines, using the "format" subdirective.  In this case
-       the commodity symbol appears twice and  should  be  the  same  in  both
-       places:
-
-              ; commodity SYMBOL
-              ;   format EXAMPLEAMOUNT
-
-              ; display indian rupees with currency name on the left,
-              ; thousands, lakhs and crores comma-separated,
-              ; period as decimal point, and two decimal places.
-              commodity INR
-                format INR 9,99,99,999.00
-
-   Default commodity
-       The  D  directive  sets a default commodity (and display format), to be
-       used for amounts without a commodity symbol (ie, plain numbers).  (Note
-       this  differs from Ledger's default commodity directive.) The commodity
-       and display format will be applied  to  all  subsequent  commodity-less
-       amounts, or until the next D directive.
-
-              # commodity-less amounts should be treated as dollars
-              # (and displayed with symbol on the left, thousands separators and two decimal places)
-              D $1,000.00
-
-              1/1
-                a     5    ; <- commodity-less amount, becomes $1
-                b
-
-   Default year
-       You  can set a default year to be used for subsequent dates which don't
-       specify a year.  This is a line beginning with Y followed by the  year.
-       Eg:
-
-              Y2009      ; set default year to 2009
-
-              12/15      ; equivalent to 2009/12/15
-                expenses  1
-                assets
-
-              Y2010      ; change default year to 2010
-
-              2009/1/30  ; specifies the year, not affected
-                expenses  1
-                assets
-
-              1/31       ; equivalent to 2010/1/31
-                expenses  1
-                assets
-
-   Including other files
-       You  can  pull in the content of additional journal files by writing an
-       include directive, like this:
-
-              include path/to/file.journal
-
-       If the path does not begin with a slash, it is relative to the  current
-       file.  Glob patterns (*) are not currently supported.
-
-       The  include  directive  can  only  be  used  in journal files.  It can
-       include journal, timeclock or timedot files, but not CSV files.
-
-Periodic transactions
-       Periodic transactions are a kind of rule with a dual purpose: they  can
-       specify  recurring  future  transactions  (with  --forecast), or budget
-       goals (with --budget).  They look a bit like a transaction, except  the
-       first line is a tilde (~) followed by a period expression:
+   Periodic transactions
+       Periodic transaction rules (enabled by --forecast or --budget) describe
+       recurring transactions.  They look like a transaction where  the  first
+       line  is  a  tilde  (~) followed by a period expression (mnemonic: ~ is
+       like a recurring sine wave):
 
               ~ weekly
                 assets:bank:checking   $400 ; paycheck
                 income:acme inc
 
-       With  --forecast,  each  periodic  transaction rule generates recurring
-       "forecast" transactions at the specified interval,  beginning  the  day
-       after  the  latest recorded journal transaction (or today, if there are
-       no transactions) and ending 6 months from today (or at the  report  end
-       date, if specified).
+       Periodic transactions have a dual purpose:
 
-       With  balance --budget,  each  periodic  transaction declares recurring
-       budget goals for the specified accounts.  Eg the example above declares
-       the  goal  of receiving $400 from income:acme inc (and also, depositing
-       $400 into assets:bank:checking) every week.
+       o With --forecast, each  periodic  transaction  rule  generates  future
+         transactions,  recurring at the specified interval, which can be seen
+         in reports.  Forecast transactions begin the  day  after  the  latest
+         recorded journal transaction (or today, if there are no transactions)
+         and end 6 months from today (or at the report  end  date,  if  speci-
+         fied).
 
-       For more details, see: balance: Budgeting and Budgeting  and  Forecast-
-       ing.
+       o With  --budget  (supported  by  the  balance  command), each periodic
+         transaction rule declares recurring budget goals  for  the  specified
+         accounts,  which can be seen in budget reports.  Eg the example above
+         declares the goal of receiving $400 from income:acme inc  (and  also,
+         depositing $400 into assets:bank:checking) every week.
 
-Automated postings
-       Automated  postings are postings added automatically by rule to certain
-       transactions (with --auto).  An automated posting  rule  looks  like  a
-       transaction  where  the  first  line is an equal sign (=) followed by a
-       query:
+       (Actually,  you  can  generate  one-off  transactions too, by writing a
+       period expression with no report interval.)
 
+       For more details, see: balance: Budget report and  Cookbook:  Budgeting
+       and Forecasting.
+
+   Automated postings
+       Automated postings (enabled by --auto) are postings added automatically
+       by rule to certain transactions.  An automated posting rule looks  like
+       a  transaction  where the first line is an equal sign (=) followed by a
+       query (mnemonic: = tests for matching transactions, and also looks like
+       posting lines):
+
               = expenses:gifts
                   budget:gifts  *-1
                   assets:budget  *1
 
-       The posting amounts can be of the form *N, which means "the  amount  of
-       the  matched  transaction's  first posting, multiplied by N".  They can
+       The  posting  amounts can be of the form *N, which means "the amount of
+       the matched transaction's first posting, multiplied by  N".   They  can
        also be ordinary fixed amounts.  Fixed amounts with no commodity symbol
-       will  be  given  the  same commodity as the matched transaction's first
+       will be given the same commodity as  the  matched  transaction's  first
        posting.
 
-       This example adds a corresponding (unbalanced) budget posting to  every
+       This  example adds a corresponding (unbalanced) budget posting to every
        transaction involving the expenses:gifts account:
 
               = expenses:gifts
@@ -897,16 +914,19 @@
               $ hledger print --auto
               2017/12/14
                   expenses:gifts             $20
-                  assets
                   (budget:gifts)            $-20
+                  assets
 
+       Like postings recorded  by  hand,  automated  postings  participate  in
+       transaction balancing, missing amount inference and balance assertions.
+
 EDITOR SUPPORT
        Add-on modes exist for various text editors, to make working with jour-
-       nal files easier.  They add colour, navigation aids  and  helpful  com-
-       mands.   For  hledger  users  who  edit  the journal file directly (the
+       nal  files  easier.   They add colour, navigation aids and helpful com-
+       mands.  For hledger users who  edit  the  journal  file  directly  (the
        majority), using one of these modes is quite recommended.
 
-       These were written with Ledger in mind,  but  also  work  with  hledger
+       These  were  written  with  Ledger  in mind, but also work with hledger
        files:
 
 
@@ -925,7 +945,7 @@
 
 
 REPORTING BUGS
-       Report  bugs at http://bugs.hledger.org (or on the #hledger IRC channel
+       Report bugs at http://bugs.hledger.org (or on the #hledger IRC  channel
        or hledger mail list)
 
 
@@ -939,7 +959,7 @@
 
 
 SEE ALSO
-       hledger(1),     hledger-ui(1),     hledger-web(1),      hledger-api(1),
+       hledger(1),      hledger-ui(1),     hledger-web(1),     hledger-api(1),
        hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_time-
        dot(5), ledger(1)
 
@@ -947,4 +967,4 @@
 
 
 
-hledger 1.9                       March 2018                hledger_journal(5)
+hledger 1.9.1                     April 2018                hledger_journal(5)
diff --git a/hledger_timeclock.5 b/hledger_timeclock.5
--- a/hledger_timeclock.5
+++ b/hledger_timeclock.5
@@ -1,5 +1,5 @@
 
-.TH "hledger_timeclock" "5" "March 2018" "hledger 1.9" "hledger User Manuals"
+.TH "hledger_timeclock" "5" "April 2018" "hledger 1.9.1" "hledger User Manuals"
 
 
 
diff --git a/hledger_timeclock.info b/hledger_timeclock.info
--- a/hledger_timeclock.info
+++ b/hledger_timeclock.info
@@ -4,8 +4,8 @@
 
 File: hledger_timeclock.info,  Node: Top,  Up: (dir)
 
-hledger_timeclock(5) hledger 1.9
-********************************
+hledger_timeclock(5) hledger 1.9.1
+**********************************
 
 hledger can read timeclock files.  As with Ledger, these are (a subset
 of) timeclock.el's format, containing clock-in and clock-out entries as
diff --git a/hledger_timeclock.txt b/hledger_timeclock.txt
--- a/hledger_timeclock.txt
+++ b/hledger_timeclock.txt
@@ -77,4 +77,4 @@
 
 
 
-hledger 1.9                       March 2018              hledger_timeclock(5)
+hledger 1.9.1                     April 2018              hledger_timeclock(5)
diff --git a/hledger_timedot.5 b/hledger_timedot.5
--- a/hledger_timedot.5
+++ b/hledger_timedot.5
@@ -1,5 +1,5 @@
 
-.TH "hledger_timedot" "5" "March 2018" "hledger 1.9" "hledger User Manuals"
+.TH "hledger_timedot" "5" "April 2018" "hledger 1.9.1" "hledger User Manuals"
 
 
 
diff --git a/hledger_timedot.info b/hledger_timedot.info
--- a/hledger_timedot.info
+++ b/hledger_timedot.info
@@ -4,8 +4,8 @@
 
 File: hledger_timedot.info,  Node: Top,  Next: FILE FORMAT,  Up: (dir)
 
-hledger_timedot(5) hledger 1.9
-******************************
+hledger_timedot(5) hledger 1.9.1
+********************************
 
 Timedot is a plain text format for logging dated, categorised quantities
 (of time, usually), supported by hledger.  It is convenient for
@@ -110,7 +110,7 @@
 
 Tag Table:
 Node: Top76
-Node: FILE FORMAT805
-Ref: #file-format906
+Node: FILE FORMAT809
+Ref: #file-format910
 
 End Tag Table
diff --git a/hledger_timedot.txt b/hledger_timedot.txt
--- a/hledger_timedot.txt
+++ b/hledger_timedot.txt
@@ -124,4 +124,4 @@
 
 
 
-hledger 1.9                       March 2018                hledger_timedot(5)
+hledger 1.9.1                     April 2018                hledger_timedot(5)
