Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package ghc-hledger-lib for openSUSE:Factory 
checked in at 2025-12-19 16:45:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ghc-hledger-lib (Old)
 and      /work/SRC/openSUSE:Factory/.ghc-hledger-lib.new.1928 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ghc-hledger-lib"

Fri Dec 19 16:45:38 2025 rev:44 rq:1323028 version:1.51.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/ghc-hledger-lib/ghc-hledger-lib.changes  
2025-11-27 15:20:01.701101551 +0100
+++ 
/work/SRC/openSUSE:Factory/.ghc-hledger-lib.new.1928/ghc-hledger-lib.changes    
    2025-12-19 16:49:02.081872879 +0100
@@ -1,0 +2,18 @@
+Mon Dec  8 21:11:05 UTC 2025 - Peter Simons <[email protected]>
+
+- Update hledger-lib to version 1.51.1.
+  Upstream's change log file format is strange (too much unmodified
+  text at at the top). The automatic updater cannot extract the
+  relevant additions. You can find the file at:
+  http://hackage.haskell.org/package/hledger-lib-1.51.1/src/CHANGES.md
+
+-------------------------------------------------------------------
+Fri Dec  5 10:48:22 UTC 2025 - Peter Simons <[email protected]>
+
+- Update hledger-lib to version 1.51.
+  Upstream's change log file format is strange (too much unmodified
+  text at at the top). The automatic updater cannot extract the
+  relevant additions. You can find the file at:
+  http://hackage.haskell.org/package/hledger-lib-1.51/src/CHANGES.md
+
+-------------------------------------------------------------------

Old:
----
  hledger-lib-1.50.3.tar.gz

New:
----
  hledger-lib-1.51.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ ghc-hledger-lib.spec ++++++
--- /var/tmp/diff_new_pack.TsrEaD/_old  2025-12-19 16:49:05.274006369 +0100
+++ /var/tmp/diff_new_pack.TsrEaD/_new  2025-12-19 16:49:05.274006369 +0100
@@ -20,7 +20,7 @@
 %global pkgver %{pkg_name}-%{version}
 %bcond_with tests
 Name:           ghc-%{pkg_name}
-Version:        1.50.3
+Version:        1.51.1
 Release:        0
 Summary:        A library providing the core functionality of hledger
 License:        GPL-3.0-or-later

++++++ hledger-lib-1.50.3.tar.gz -> hledger-lib-1.51.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/CHANGES.md 
new/hledger-lib-1.51.1/CHANGES.md
--- old/hledger-lib-1.50.3/CHANGES.md   2025-11-18 23:54:26.000000000 +0100
+++ new/hledger-lib-1.51.1/CHANGES.md   2025-12-08 22:04:20.000000000 +0100
@@ -13,10 +13,31 @@
 
 -->
 
-Internal/api/developer-ish changes in the hledger-lib (and hledger) packages.
+API/developer-ish changes in hledger-lib.
 For user-visible changes, see the hledger package changelog.
 
 
+# 1.51.1 2025-12-08
+
+
+# 1.51 2025-12-05
+
+Breaking changes
+
+- Hledger.Data.Balancing: balanceTransaction -> balanceSingleTransaction
+- Hledger.Utils.IO:
+  - inputToHandle -> textToHandle; set utf8 not utf8_bom
+  - readHandlePortably, readHandlePortably' -> hGetContentsPortably
+
+Improvements
+
+- Hledger.Utils.String:
+  quoteForCommandLine now quotes some additional problem characters, and no 
longer quotes "7".
+  [#2468]
+
+
+# 1.50.4 2025-12-04
+
 # 1.50.3 2025-11-18
 
 # 1.50.2 2025-09-26
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/Hledger/Data/Balancing.hs 
new/hledger-lib-1.51.1/Hledger/Data/Balancing.hs
--- old/hledger-lib-1.50.3/Hledger/Data/Balancing.hs    2025-11-18 
23:54:26.000000000 +0100
+++ new/hledger-lib-1.51.1/Hledger/Data/Balancing.hs    2025-12-08 
22:03:33.000000000 +0100
@@ -16,7 +16,7 @@
 , defbalancingopts
   -- * transaction balancing
 , isTransactionBalanced
-, balanceTransaction
+, balanceSingleTransaction
 , balanceTransactionHelper
   -- * assertion validation
 , transactionCheckAssertions
@@ -173,32 +173,21 @@
       Right _ -> Right t
       Left e -> Left e
 
--- | Balance this transaction, ensuring that its postings
+-- | Balance this isolated transaction, ensuring that its postings
 -- (and its balanced virtual postings) sum to 0,
 -- by inferring a missing amount or conversion price(s) if needed.
 -- Or if balancing is not possible, because the amounts don't sum to 0 or
 -- because there's more than one missing amount, return an error message.
 --
--- Transactions with balance assignments can have more than one
--- missing amount; to balance those you should use the more powerful
--- journalBalanceTransactions.
---
--- The "sum to 0" test is done using commodity display precisions,
--- if provided, so that the result agrees with the numbers users can see.
---
-balanceTransaction ::
-     BalancingOpts
-  -> Transaction
-  -> Either String Transaction
-balanceTransaction bopts = fmap fst . balanceTransactionHelper bopts
+-- Note this is not as accurate as @balanceTransactionInJournal@,
+-- which considers the whole journal when calculating balance assignments and 
balance assertions.
+balanceSingleTransaction :: BalancingOpts -> Transaction -> Either String 
Transaction
+balanceSingleTransaction bopts = fmap fst . balanceTransactionHelper bopts
 
--- | Helper used by balanceTransaction and 
balanceTransactionWithBalanceAssignmentAndCheckAssertionsB;
+-- | Helper used by balanceSingleTransaction and 
balanceTransactionWithBalanceAssignmentAndCheckAssertionsB;
 -- use one of those instead.
 -- It also returns a list of accounts and amounts that were inferred.
-balanceTransactionHelper ::
-     BalancingOpts
-  -> Transaction
-  -> Either String (Transaction, [(AccountName, MixedAmount)])
+balanceTransactionHelper :: BalancingOpts -> Transaction -> Either String 
(Transaction, [(AccountName, MixedAmount)])
 balanceTransactionHelper bopts t = do
   let lbl = lbl_ "balanceTransactionHelper"
   (t', inferredamtsandaccts) <- t
@@ -411,7 +400,7 @@
 --   journalBalanceTransactions
 --    runST
 --     runExceptT
---      balanceTransaction (Transaction.hs)
+--      balanceSingleTransaction (Transaction.hs)
 --       balanceTransactionHelper
 --      runReaderT
 --       balanceTransactionAndCheckAssertionsB
@@ -422,7 +411,7 @@
 --   journalCheckBalanceAssertions
 --    journalBalanceTransactions
 --  transactionWizard, postingsBalanced (Add.hs), tests (Transaction.hs)
---   balanceTransaction (Transaction.hs)  XXX hledger add won't allow balance 
assignments + missing amount ?
+--   balanceSingleTransaction (Transaction.hs)  XXX hledger add won't allow 
balance assignments + missing amount ?
 
 -- | Monad used for statefully balancing/amount-inferring/assertion-checking
 -- a sequence of transactions.
@@ -532,7 +521,7 @@
         -- postponing those which do until later. The balanced ones are split 
into their postings,
         -- keeping these and the not-yet-balanced transactions in the same 
relative order.
         psandts :: [Either Posting Transaction] <- fmap concat $ forM ts $ 
\case
-          t | null $ assignmentPostings t -> case balanceTransaction bopts t of
+          t | null $ assignmentPostings t -> case balanceSingleTransaction 
bopts t of
               Left  e  -> throwError e
               Right t' -> do
                 lift $ writeArray balancedtxns (tindex t') t'
@@ -810,10 +799,10 @@
          (fst <$> transactionInferBalancingAmount M.empty 
nulltransaction{tpostings = ["a" `post` usd (-5), "b" `post` (eur 3 @@ usd 4), 
"c" `post` missingamt]}) @?=
            Right nulltransaction{tpostings = ["a" `post` usd (-5), "b" `post` 
(eur 3 @@ usd 4), "c" `post` usd 1]}
 
-    , testGroup "balanceTransaction" [
+    , testGroup "balanceSingleTransaction" [
          testCase "detect unbalanced entry, sign error" $
           assertLeft
-            (balanceTransaction defbalancingopts
+            (balanceSingleTransaction defbalancingopts
                (Transaction
                   0
                   ""
@@ -828,7 +817,7 @@
                   [posting {paccount = "a", pamount = mixedAmount (usd 1)}, 
posting {paccount = "b", pamount = mixedAmount (usd 1)}]))
         ,testCase "detect unbalanced entry, multiple missing amounts" $
           assertLeft $
-             balanceTransaction defbalancingopts
+             balanceSingleTransaction defbalancingopts
                (Transaction
                   0
                   ""
@@ -845,7 +834,7 @@
                   ])
         ,testCase "one missing amount is inferred" $
           (pamount . last . tpostings <$>
-           balanceTransaction defbalancingopts
+           balanceSingleTransaction defbalancingopts
              (Transaction
                 0
                 ""
@@ -861,7 +850,7 @@
           Right (mixedAmount $ usd (-1))
         ,testCase "conversion price is inferred" $
           (pamount . headErr . tpostings <$>  -- PARTIAL headErr succeeds 
because non-null postings list
-           balanceTransaction defbalancingopts
+           balanceSingleTransaction defbalancingopts
              (Transaction
                 0
                 ""
@@ -877,9 +866,9 @@
                 , posting {paccount = "b", pamount = mixedAmount (eur (-1))}
                 ])) @?=
           Right (mixedAmount $ usd 1.35 @@ eur 1)
-        ,testCase "balanceTransaction balances based on cost if there are unit 
prices" $
+        ,testCase "balanceSingleTransaction balances based on cost if there 
are unit prices" $
           assertRight $
-          balanceTransaction defbalancingopts
+          balanceSingleTransaction defbalancingopts
             (Transaction
                0
                ""
@@ -894,9 +883,9 @@
                [ posting {paccount = "a", pamount = mixedAmount $ usd 1 `at` 
eur 2}
                , posting {paccount = "a", pamount = mixedAmount $ usd (-2) 
`at` eur 1}
                ])
-        ,testCase "balanceTransaction balances based on cost if there are 
total prices" $
+        ,testCase "balanceSingleTransaction balances based on cost if there 
are total prices" $
           assertRight $
-          balanceTransaction defbalancingopts
+          balanceSingleTransaction defbalancingopts
             (Transaction
                0
                ""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/Hledger/Data/Journal.hs 
new/hledger-lib-1.51.1/Hledger/Data/Journal.hs
--- old/hledger-lib-1.50.3/Hledger/Data/Journal.hs      2025-11-18 
23:54:26.000000000 +0100
+++ new/hledger-lib-1.51.1/Hledger/Data/Journal.hs      2025-12-08 
22:03:33.000000000 +0100
@@ -1153,6 +1153,7 @@
 -- "comm" and "cur" are accepted as synonyms meaning the commodity symbol.
 -- Pivoting on an unknown field or tag, or on commodity when there are 
multiple commodities, returns "".
 -- Pivoting on a tag when there are multiple values for that tag, returns the 
first value.
+-- Pivoting on the "type" tag normalises type values to their short spelling.
 pivotComponent :: Text -> Posting -> Text
 pivotComponent fieldortagname p
   | fieldortagname == "code",        Just t <- ptransaction p = tcode t
@@ -1164,7 +1165,10 @@
   | fieldortagname `elem` commnames = case map acommodity $ amounts $ pamount 
p of [s] -> s; _ -> unknown
   | fieldortagname == "amt"         = case amounts $ pamount p of [a] -> 
T.pack $ show $ aquantity a; _ -> unknown
   | fieldortagname == "cost"        = case amounts $ pamount p of 
[a@Amount{acost=Just _}] -> T.pack $ lstrip $ showAmountCost a; _ -> unknown
-  | Just (_, tagvalue) <- postingFindTag fieldortagname p = tagvalue
+  | Just (_, tagvalue) <- postingFindTag fieldortagname p =
+      if fieldortagname == "type"
+      then either (const tagvalue) (T.pack . show) $ parseAccountType True 
tagvalue
+      else tagvalue
   | otherwise = unknown
   where
     descnames = ["desc", "description"]   -- allow "description" for hledger 
<=1.30 compat
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/Hledger/Read/Common.hs 
new/hledger-lib-1.51.1/Hledger/Read/Common.hs
--- old/hledger-lib-1.50.3/Hledger/Read/Common.hs       2025-11-18 
23:54:26.000000000 +0100
+++ new/hledger-lib-1.51.1/Hledger/Read/Common.hs       2025-12-08 
22:03:33.000000000 +0100
@@ -262,7 +262,7 @@
 
 handleReadFnToTextReadFn :: (InputOpts -> FilePath -> Text -> ExceptT String 
IO Journal) -> InputOpts -> FilePath -> Handle -> ExceptT String IO Journal
 handleReadFnToTextReadFn p iopts fp =
-  p iopts fp <=< lift . readHandlePortably
+  p iopts fp <=< lift . hGetContentsPortably Nothing
 
 -- | Get the date span from --forecast's PERIODEXPR argument, if any.
 -- This will fail with a usage error if the period expression cannot be parsed,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/Hledger/Read/CsvReader.hs 
new/hledger-lib-1.51.1/Hledger/Read/CsvReader.hs
--- old/hledger-lib-1.50.3/Hledger/Read/CsvReader.hs    2025-11-18 
23:54:26.000000000 +0100
+++ new/hledger-lib-1.51.1/Hledger/Read/CsvReader.hs    2025-12-08 
22:03:33.000000000 +0100
@@ -63,7 +63,7 @@
 parse sep iopts f h = do
   rules <- readRules $ getRulesFile f (mrules_file_ iopts)
   mencoding <- rulesEncoding rules
-  csvtext <- lift $ readHandlePortably' mencoding h
+  csvtext <- lift $ hGetContentsPortably mencoding h
   readJournalFromCsv rules f csvtext (Just sep)
   -- apply any command line account aliases. Can fail with a bad replacement 
pattern.
   >>= liftEither . journalApplyAliases (aliasesFromOpts iopts)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/Hledger/Read/JournalReader.hs 
new/hledger-lib-1.51.1/Hledger/Read/JournalReader.hs
--- old/hledger-lib-1.50.3/Hledger/Read/JournalReader.hs        2025-11-18 
20:00:30.000000000 +0100
+++ new/hledger-lib-1.51.1/Hledger/Read/JournalReader.hs        2025-12-08 
22:03:33.000000000 +0100
@@ -32,6 +32,7 @@
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE PackageImports      #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE MultiWayIf #-}
 
 --- ** exports
 module Hledger.Read.JournalReader (
@@ -72,13 +73,14 @@
 
 --- ** imports
 import Control.Exception qualified as C
-import Control.Monad (forM_, when, void, unless, filterM)
+import Control.Monad (forM_, when, void, unless, filterM, forM)
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import Control.Monad.Except (ExceptT(..), runExceptT)
 import Control.Monad.State.Strict (evalStateT,get,modify',put)
 import Control.Monad.Trans.Class (lift)
 import Data.Char (toLower)
 import Data.Either (isRight, lefts)
+import Data.Functor ((<&>))
 import Data.Map.Strict qualified as M
 import Data.Text (Text)
 import Data.String
@@ -91,6 +93,7 @@
 import Text.Megaparsec hiding (parse)
 import Text.Megaparsec.Char
 import Text.Printf
+import System.Directory (canonicalizePath, doesFileExist, makeAbsolute)
 import System.FilePath
 import "Glob" System.FilePath.Glob hiding (match)
 -- import "filepattern" System.FilePattern.Directory
@@ -103,8 +106,7 @@
 import Hledger.Read.RulesReader qualified as RulesReader (reader)
 import Hledger.Read.TimeclockReader qualified as TimeclockReader (reader)
 import Hledger.Read.TimedotReader qualified as TimedotReader (reader)
-import System.Directory (canonicalizePath, doesFileExist)
-import Data.Functor ((<&>))
+import Data.Function ((&))
 
 --- ** doctest setup
 -- $setup
@@ -304,21 +306,26 @@
   -- save the position at start of include directive, for error messages
   eoff <- getOffset
   pos <- getSourcePos
+  let errorNoArg = customFailure $ parseErrorAt eoff "include needs a file 
path or glob pattern argument"
 
   -- parse the directive
   string "include"
-  lift skipNonNewlineSpaces1
-  prefixedglob <- rstrip . T.unpack <$> takeWhileP Nothing (`notElem` 
[';','\n'])
-  lift followingcommentp
+  -- notFollowedBy newline <?> "a file path or glob pattern argument"
+  prefixedglob <- (do
+    lift skipNonNewlineSpaces1
+    prefixedglob <- rstrip . T.unpack <$> takeWhileP Nothing (`notElem` 
[';','\n'])
+    lift followingcommentp
+    return prefixedglob
+    ) <|> errorNoArg
+
   let (mprefix,glb) = splitReaderPrefix prefixedglob
-  parentf <- sourcePosFilePath pos   -- a little slow, don't do too often
-  when (null $ dbg6 (parentf <> " include: glob pattern") glb) $
-    customFailure $ parseErrorAt eoff $ "include needs a file path or glob 
pattern argument"
+  parentf <- sourcePosFilePath pos
+  when (null $ dbg6 (parentf <> " include: glob pattern") glb) errorNoArg
 
   -- Find the file or glob-matched files (just the ones from this include 
directive), with some IO error checking.
+  paths <- findMatchedFiles eoff parentf glb
   -- Also report whether a glob pattern was used, and not just a literal file 
path.
   -- (paths, isglob) <- findMatchedFiles off pos glb
-  paths <- findMatchedFiles eoff parentf glb
 
   -- XXX worth the trouble ? no
   -- Comprehensively exclude files already processed. Some complexities here:
@@ -348,12 +355,8 @@
     -- Converts ** without a slash to **/*, like zsh's GLOB_STAR_SHORT, so ** 
also matches file name parts.
     -- Checks if any matched paths are directories and excludes those.
     -- Converts all matched paths to their canonical form.
-    --
-    -- Glob patterns never match dot files or files under dot directories,
-    -- even if it seems like they should; this is a workaround for Glob bug 
#49.
-    -- This workaround is disabled if the --old-glob flag is present in the 
command line
-    -- (detected with unsafePerformIO; it's not worth a ton of boilerplate).
-    -- In that case, be aware ** recursive globs will search intermediate dot 
directories.
+    -- Note * and ** mostly won't implicitly match dot files or dot 
directories,
+    -- but ** will implicitly search non-top-level dot directories (see #2498, 
Glob#49).
 
     findMatchedFiles :: (MonadIO m) => Int -> FilePath -> FilePath -> 
JournalParser m [FilePath]
     findMatchedFiles off parentf globpattern = do
@@ -369,14 +372,15 @@
       -- * at the start of a file name ignores dot-named files and 
directories, by default.
       -- ** (or zero or more consecutive *'s) not followed by slash is 
equivalent to *.
       -- A **/ component matches any number of directory parts.
-      -- A **/ ignores dot-named directories in its starting and ending 
directories, by default.
-      -- But **/ does search intermediate dot-named directories. Eg it can 
find a/.b/c.
+      -- A **/ does not implicitly search top-level dot directories or 
implicitly match do files,
+      -- but it does search non-top-level dot directories. Eg ** will find the 
c file in a/.b/c.
 
       -- expand a tilde at the start of the glob pattern, or throw an error
-      expandedglob <- lift $ expandHomePath globpattern `orRethrowIOError` 
"failed to expand ~"
+      expandedglob <- lift $ expandHomePath globpattern & handleIOError off 
"failed to expand ~"
 
       -- get the directory of the including file
-      let cwd = takeDirectory parentf
+      -- need to canonicalise a symlink parentf so takeDirectory works 
correctly [#2503]
+      cwd <- fmap takeDirectory <$> liftIO $ canonicalizePath parentf
 
       -- Don't allow 3 or more stars.
       when ("***" `isInfixOf` expandedglob) $
@@ -394,7 +398,6 @@
       g <- case tryCompileWith compDefault{errorRecovery=False} expandedglob' 
of
         Left e  -> customFailure $ parseErrorAt off $ "Invalid glob pattern: " 
++ e
         Right x -> pure x
-      let isglob = not $ isLiteral g
 
       -- Find all matched paths. These might include directories or the 
current file.
       paths <- liftIO $ globDir1 g cwd
@@ -402,49 +405,44 @@
       -- Exclude any directories or symlinks to directories, and canonicalise, 
and sort.
       files <- liftIO $
         filterM doesFileExist paths
-        >>= mapM canonicalizePath
+        >>= mapM makeAbsolute
         <&> sort
 
-      -- Work around a Glob bug with dot dirs: while **/ ignores dot dirs in 
the starting and ending dirs,
-      -- it does search dot dirs in between those two (Glob #49).
-      -- This could be inconvenient, eg making it hard to avoid VCS 
directories in a source tree.
-      -- We work around as follows: when any glob was used, paths involving 
dot dirs are excluded in post processing.
-      -- Unfortunately this means valid globs like .dotdir/* can't be used; 
only literal paths can match
-      -- things in dot dirs. An --old-glob command line flag disables this 
workaround, for backward compatibility.
-      oldglobflag <- liftIO $ getFlag ["old-glob"]
-      let
-        files2 = (if isglob && not oldglobflag then filter (not.hasdotdir) 
else id) files
-          where
-            hasdotdir p = any isdotdir $ splitPath p
-              where
-                isdotdir c = "." `isPrefixOf` c && "/" `isSuffixOf` c
+      -- Throw an error if one of these files is among the grandparent files, 
forming a cycle.
+      -- Though, ignore the immediate parent file for convenience. XXX 
inconsistent - should it ignore all cyclic includes ?
+      -- We used to store the canonical paths, then switched to non-canonical 
paths for more useful output,
+      -- which means for each include directive we must re-canonicalise 
everything here; noticeable ? XXX
+      parentj <- get
+      let parentfiles = jincludefilestack parentj
+      cparentfiles <- liftIO $ mapM canonicalizePath parentfiles
+      let cparentf = take 1 parentfiles
+      files2 <- forM files $ \f -> do
+        cf <- liftIO $ canonicalizePath f
+        if
+          | [cf] == cparentf -> return cf  -- current file - return 
canonicalised, will be excluded later
+          | cf `elem` drop 1 cparentfiles -> customFailure $ parseErrorAt off 
$ "This included file forms a cycle: " ++ f
+          | otherwise -> return f
 
       -- Throw an error if no files were matched.
-      when (null files2) $
-        customFailure $ parseErrorAt off $ "No files were matched by glob 
pattern: " ++ globpattern
+      when (null files2) $ customFailure $ parseErrorAt off $ "No files were 
matched by: " ++ globpattern
 
-      -- If a glob was used, exclude the current file, for convenience.
+      -- If the current file got included, ignore it (last, to avoid 
triggering the error above).
       let
         files3 =
-          dbg6 (parentf <> " include: matched files" <> if isglob then " 
(excluding current file)" else "") $
-          (if isglob then filter (/= parentf) else id) files2
+          dbg6 (parentf <> " include: matched files (excluding current file)") 
$
+          filter (not.(`elem` cparentf)) files2
 
       return files3
 
     -- Parse the given included file (and any deeper includes, recursively) as 
if it was inlined in the current (parent) file.
     -- The offset of the start of the include directive in the parent file is 
provided for error messages.
     parseIncludedFile :: MonadIO m => InputOpts -> Int -> PrefixedFilePath -> 
ErroringJournalParser m ()
-    parseIncludedFile iopts1 eoff prefixedpath = do
+    parseIncludedFile iopts1 off prefixedpath = do
       let (_mprefix,filepath) = splitReaderPrefix prefixedpath
 
-      -- Throw an error if a cycle is detected
-      parentj <- get
-      let parentfilestack = jincludefilestack parentj
-      when (dbg7 "parseIncludedFile: reading" filepath `elem` parentfilestack) 
$
-        customFailure $ parseErrorAt eoff $ "This included file forms a cycle: 
" ++ filepath
-
       -- Read the file's content, or throw an error
-      childInput <- lift $ readFilePortably filepath `orRethrowIOError` 
"failed to read a file"
+      childInput <- lift $ readFilePortably filepath & handleIOError off 
"failed to read a file"
+      parentj <- get
       let initChildj = newJournalWithParseStateFrom filepath parentj
 
       -- Choose a reader based on the file path prefix or file extension,
@@ -491,22 +489,20 @@
           ,jincludefilestack      = filepath : jincludefilestack j
           }
 
--- Get the canonical path of the file referenced by this parse position.
--- Symbolic links will be dereferenced. This probably will always succeed
--- (since the parse file's path is probably always absolute).
+-- Get the absolute path of the file referenced by this parse position.
+-- (Symbolic links will not be dereferenced.)
+-- This probably will always succeed, since the parse file's path is probably 
always absolute.
 sourcePosFilePath :: (MonadIO m) => SourcePos -> m FilePath
-sourcePosFilePath = liftIO . canonicalizePath . sourceName
--- "canonicalizePath is a very big hammer. If you only need an absolute path, 
makeAbsolute is sufficient"
--- but we only do this once per include directive, seems ok to leave it as is.
-
--- | Lift an IO action into the exception monad, rethrowing any IO
--- error with the given message prepended.
-orRethrowIOError :: MonadIO m => IO a -> String -> TextParser m a
-orRethrowIOError io msg = do
+sourcePosFilePath = liftIO . makeAbsolute . sourceName
+
+-- | Lift an IO action into the exception monad, converting any IO error
+-- to a parse error message at the given offset.
+handleIOError :: MonadIO m => Int -> String -> IO a -> TextParser m a
+handleIOError off msg io = do
   eResult <- liftIO $ (Right <$> io) `C.catch` \(e::C.IOException) -> pure $ 
Left $ printf "%s:\n%s" msg (show e)
   case eResult of
     Right res -> pure res
-    Left errMsg -> fail errMsg
+    Left errMsg -> setOffset off >> fail errMsg
 
 -- Parse an account directive, adding its info to the journal's
 -- list of account declarations.
@@ -1258,8 +1254,8 @@
      assertParse ignoredpricecommoditydirectivep "N $\n"
 
   ,testGroup "includedirectivep" [
-      testCase "include" $ assertParseErrorE (includedirectivep definputopts) 
"include nosuchfile\n" "No files were matched by glob pattern: nosuchfile"
-     ,testCase "glob" $ assertParseErrorE (includedirectivep definputopts) 
"include nosuchfile*\n" "No files were matched by glob pattern: nosuchfile*"
+      testCase "include" $ assertParseErrorE (includedirectivep definputopts) 
"include nosuchfile\n" "No files were matched by: nosuchfile"
+     ,testCase "glob" $ assertParseErrorE (includedirectivep definputopts) 
"include nosuchfile*\n" "No files were matched by: nosuchfile*"
      ]
 
   ,testCase "marketpricedirectivep" $ assertParseEq marketpricedirectivep
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/Hledger/Read/RulesReader.hs 
new/hledger-lib-1.51.1/Hledger/Read/RulesReader.hs
--- old/hledger-lib-1.50.3/Hledger/Read/RulesReader.hs  2025-11-18 
23:54:26.000000000 +0100
+++ new/hledger-lib-1.51.1/Hledger/Read/RulesReader.hs  2025-12-08 
22:03:33.000000000 +0100
@@ -235,7 +235,7 @@
     (_, Just f,  mc) -> do  -- trace "file found" $
       mencoding <- rulesEncoding rules
       liftIO $ do
-        raw <- openFileOrStdin f >>= readHandlePortably' mencoding
+        raw <- openFileOrStdin f >>= hGetContentsPortably mencoding
         maybe (return raw) (\c -> runCommandAsFilter rulesfile (dbg0Msg 
("running: "++c) c) raw) mc
 
     -- no file pattern, but a data generating command
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/Hledger/Read.hs 
new/hledger-lib-1.51.1/Hledger/Read.hs
--- old/hledger-lib-1.50.3/Hledger/Read.hs      2025-11-18 23:54:26.000000000 
+0100
+++ new/hledger-lib-1.51.1/Hledger/Read.hs      2025-12-08 22:03:33.000000000 
+0100
@@ -133,7 +133,7 @@
 
 --- ** imports
 import Control.Exception qualified as C
-import Control.Monad (unless, when, forM, (<=<))
+import Control.Monad (unless, when, forM, (>=>))
 import "mtl" Control.Monad.Except (ExceptT(..), runExceptT, liftEither)
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import Data.Default (def)
@@ -167,6 +167,7 @@
 import Prelude hiding (getContents, writeFile)
 import Hledger.Data.JournalChecks (journalStrictChecks)
 import Text.Printf (printf)
+import Hledger.Data.Journal (journalNumberTransactions)
 
 --- ** doctest setup
 -- $setup
@@ -223,7 +224,7 @@
     exists <- doesFileExist defaultfile
     if exists then return defaultfile
     -- else error' $ "LEDGER_FILE is unset and \"" <> defaultfile <> "\" was 
not found"
-    else error' $ "neither LEDGER_FILE nor \"" <> defaultfile <> "\" were 
found"
+    else error' $ "neither LEDGER_FILE nor \"" <> defaultfile <> "\" was found"
   else do
     mf <- headMay <$> expandGlob "." ledgerfile `C.catch` (\(_::C.IOException) 
-> return [])
     case mf of
@@ -350,10 +351,12 @@
 -- The implementation of readJournalFiles.
 -- With --new, it also returns the latest transaction date(s) read in each file
 -- (used by the import command).
+-- This also renumbers the transactions, ensuring their tindex values are 
unique;
+-- that's also done elsewhere, but some code (accountTransactionsReport) needs 
it done sooner.
 readJournalFilesAndLatestDates :: InputOpts -> [PrefixedFilePath] -> ExceptT 
String IO (Journal, [LatestDatesForFile])
 readJournalFilesAndLatestDates iopts pfs = do
   (js, lastdates) <- unzip <$> mapM (readJournalFileAndLatestDates iopts) pfs
-  return (maybe def sconcat $ nonEmpty js, catMaybes lastdates)
+  return (journalNumberTransactions $ maybe def sconcat $ nonEmpty js, 
catMaybes lastdates)
 
 -- | An easy version of 'readJournal' which assumes default options, and fails 
in the IO monad.
 readJournal' :: Handle -> IO Journal
@@ -361,7 +364,7 @@
 
 -- | An even easier version of readJournal' which takes a 'Text' instead of a 
'Handle'.
 readJournal'' :: Text -> IO Journal
-readJournal'' = readJournal' <=< inputToHandle
+readJournal'' = textToHandle >=> readJournal'
 
 -- | An easy version of 'readJournalFile' which assumes default options, and 
fails
 -- in the IO monad.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/Hledger/Utils/IO.hs 
new/hledger-lib-1.51.1/Hledger/Utils/IO.hs
--- old/hledger-lib-1.50.3/Hledger/Utils/IO.hs  2025-11-18 23:54:26.000000000 
+0100
+++ new/hledger-lib-1.51.1/Hledger/Utils/IO.hs  2025-12-08 22:03:33.000000000 
+0100
@@ -46,10 +46,9 @@
   readFileOrStdinPortably',
   readFileStrictly,
   readFilePortably,
-  readHandlePortably,
-  readHandlePortably',
+  hGetContentsPortably,
   -- hereFileRelative,
-  inputToHandle,
+  textToHandle,
 
   -- * Command line parsing
   progArgs,
@@ -154,7 +153,7 @@
 import           System.FilePath (isRelative, (</>))
 import "Glob"    System.FilePath.Glob (glob)
 import           System.Info (os)
-import           System.IO (Handle, IOMode (..), hClose, hGetEncoding, 
hIsTerminalDevice, hPutStr, hPutStrLn, hSetNewlineMode, hSetEncoding, openFile, 
stderr, stdin, stdout, universalNewlineMode, utf8_bom)
+import           System.IO (Handle, IOMode (..), hClose, hGetEncoding, 
hIsTerminalDevice, hPutStr, hPutStrLn, hSetNewlineMode, hSetEncoding, openFile, 
stderr, stdin, stdout, universalNewlineMode, utf8_bom, utf8)
 import System.IO.Encoding qualified as Enc
 import           System.IO.Unsafe (unsafePerformIO)
 import           System.Process (CreateProcess(..), StdStream(CreatePipe), 
createPipe, shell, waitForProcess, withCreateProcess)
@@ -417,6 +416,7 @@
 -- | Like expandPath, but treats the expanded path as a glob, and returns
 -- zero or more matched absolute file paths, alphabetically sorted.
 -- Can raise an error.
+-- For a more elaborate glob expander, see 'findMatchedFiles' (used by the 
include directive).
 expandGlob :: FilePath -> FilePath -> IO [FilePath]
 expandGlob curdir p = expandPath curdir p >>= glob <&> sort  -- PARTIAL:
 
@@ -435,7 +435,7 @@
 -- using the system locale's text encoding,
 -- ignoring any utf8 BOM prefix (as seen in paypal's 2018 CSV, eg) if that 
encoding is utf8.
 readFilePortably :: FilePath -> IO T.Text
-readFilePortably f =  openFile f ReadMode >>= readHandlePortably
+readFilePortably f = openFile f ReadMode >>= hGetContentsPortably Nothing
 
 -- | Like readFilePortably, but read from standard input if the path is "-".
 readFileOrStdinPortably :: String -> IO T.Text
@@ -443,7 +443,7 @@
 
 -- | Like readFileOrStdinPortably, but take an optional converter.
 readFileOrStdinPortably' :: Maybe DynEncoding -> String -> IO T.Text
-readFileOrStdinPortably' c f = openFileOrStdin f >>= readHandlePortably' c
+readFileOrStdinPortably' c f = openFileOrStdin f >>= hGetContentsPortably c
 
 -- | Open a file for reading, using the standard System.IO.openFile.
 -- This opens the handle in text mode, using the initial system locale's text 
encoding.
@@ -451,32 +451,27 @@
 openFileOrStdin "-" = return stdin
 openFileOrStdin f' = openFile f' ReadMode
 
--- readHandlePortably' with no text encoding specified.
-readHandlePortably :: Handle -> IO T.Text
-readHandlePortably = readHandlePortably' Nothing
-
--- | Read text from a handle with a specified encoding, using the encoding 
package.
--- Or if no encoding is specified, it uses the handle's current encoding,
--- after first changing it to UTF-8BOM if it was UTF-8, to allow a Byte Order 
Mark at the start.
+-- | Read text from a handle, perhaps using a specified encoding from the 
encoding package.
+-- Or if no encoding is specified, using the handle's current encoding,
+-- changing it to UTF-8BOM if it was UTF-8, to ignore any Byte Order Mark at 
the start.
 -- Also it converts Windows line endings to newlines.
 -- If decoding fails, this throws an IOException (or possibly a 
UnicodeException or something else from the encoding package).
-readHandlePortably' :: Maybe DynEncoding -> Handle -> IO T.Text
-readHandlePortably' Nothing h = do
+hGetContentsPortably :: Maybe DynEncoding -> Handle -> IO T.Text
+hGetContentsPortably Nothing h = do
   hSetNewlineMode h universalNewlineMode
   menc <- hGetEncoding h
   when (fmap show menc == Just "UTF-8") $ hSetEncoding h utf8_bom
   T.hGetContents h
-readHandlePortably' (Just e) h =
+hGetContentsPortably (Just e) h =
   -- convert newlines manually, because Enc.hGetContents uses bytestring's 
hGetContents
   T.replace "\r\n" "\n" . T.pack <$> let ?enc = e in Enc.hGetContents h
 
--- | Create a handle from which the given text can be read.
--- Its encoding will be UTF-8BOM.
-inputToHandle :: T.Text -> IO Handle
-inputToHandle t = do
+-- | Create a handle from which the given text can be read. Its encoding will 
be UTF-8.
+textToHandle :: T.Text -> IO Handle
+textToHandle t = do
   (r, w) <- createPipe
-  hSetEncoding r utf8_bom
-  hSetEncoding w utf8_bom
+  hSetEncoding r utf8
+  hSetEncoding w utf8
   -- use a separate thread so that we don't deadlock if we can't write all of 
the text at once
   forkIO $ T.hPutStr w t >> hClose w
   return r
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hledger-lib-1.50.3/hledger-lib.cabal 
new/hledger-lib-1.51.1/hledger-lib.cabal
--- old/hledger-lib-1.50.3/hledger-lib.cabal    2025-11-18 23:54:26.000000000 
+0100
+++ new/hledger-lib-1.51.1/hledger-lib.cabal    2025-12-08 22:04:20.000000000 
+0100
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           hledger-lib
-version:        1.50.3
+version:        1.51.1
 synopsis:       A library providing the core functionality of hledger
 description:    This library contains hledger's core functionality.
                 It is used by most hledger* packages so that they support the 
same
@@ -46,7 +46,7 @@
   location: https://github.com/simonmichael/hledger
 
 flag debug
-  description: Build with GHC 9.10+'s stack traces enabled
+  description: Build with GHC 9.10+ stack traces enabled
   manual: True
   default: False
 
@@ -57,9 +57,11 @@
       Hledger.Data.Account
       Hledger.Data.AccountName
       Hledger.Data.Amount
+      Hledger.Data.BalanceData
       Hledger.Data.Balancing
       Hledger.Data.Currency
       Hledger.Data.Dates
+      Hledger.Data.DayPartition
       Hledger.Data.Errors
       Hledger.Data.Journal
       Hledger.Data.JournalChecks
@@ -68,10 +70,11 @@
       Hledger.Data.Json
       Hledger.Data.Ledger
       Hledger.Data.Period
+      Hledger.Data.PeriodData
       Hledger.Data.PeriodicTransaction
-      Hledger.Data.StringFormat
       Hledger.Data.Posting
       Hledger.Data.RawOptions
+      Hledger.Data.StringFormat
       Hledger.Data.Timeclock
       Hledger.Data.Transaction
       Hledger.Data.TransactionModifier
@@ -84,26 +87,17 @@
       Hledger.Read.InputOptions
       Hledger.Read.JournalReader
       Hledger.Read.RulesReader
-      Hledger.Read.TimedotReader
       Hledger.Read.TimeclockReader
-      Hledger.Write.Beancount
-      Hledger.Write.Csv
-      Hledger.Write.Ods
-      Hledger.Write.Html
-      Hledger.Write.Html.Attribute
-      Hledger.Write.Html.Blaze
-      Hledger.Write.Html.Lucid
-      Hledger.Write.Html.HtmlCommon
-      Hledger.Write.Spreadsheet
+      Hledger.Read.TimedotReader
       Hledger.Reports
-      Hledger.Reports.ReportOptions
-      Hledger.Reports.ReportTypes
       Hledger.Reports.AccountTransactionsReport
       Hledger.Reports.BalanceReport
       Hledger.Reports.BudgetReport
       Hledger.Reports.EntriesReport
       Hledger.Reports.MultiBalanceReport
       Hledger.Reports.PostingsReport
+      Hledger.Reports.ReportOptions
+      Hledger.Reports.ReportTypes
       Hledger.Utils
       Hledger.Utils.Debug
       Hledger.Utils.IO
@@ -112,18 +106,25 @@
       Hledger.Utils.String
       Hledger.Utils.Test
       Hledger.Utils.Text
+      Hledger.Write.Beancount
+      Hledger.Write.Csv
+      Hledger.Write.Html
+      Hledger.Write.Html.Attribute
+      Hledger.Write.Html.Blaze
+      Hledger.Write.Html.HtmlCommon
+      Hledger.Write.Html.Lucid
+      Hledger.Write.Ods
+      Hledger.Write.Spreadsheet
       Text.Tabular.AsciiWide
       Text.WideString
   other-modules:
-      Hledger.Data.BalanceData
-      Hledger.Data.DayPartition
-      Hledger.Data.PeriodData
       Paths_hledger_lib
   autogen-modules:
       Paths_hledger_lib
   hs-source-dirs:
       ./
   ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures 
-Wno-orphans -Wno-type-defaults -Wno-unused-do-bind
+  cpp-options: -DVERSION="1.51.1"
   build-depends:
       Decimal >=0.5.1
     , Glob >=0.9
@@ -183,6 +184,7 @@
   hs-source-dirs:
       test
   ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures 
-Wno-orphans -Wno-type-defaults -Wno-unused-do-bind
+  cpp-options: -DVERSION="1.51.1"
   build-depends:
       Decimal >=0.5.1
     , Glob >=0.7
@@ -236,8 +238,6 @@
   default-language: GHC2021
   if (flag(debug))
     cpp-options: -DDEBUG
-  if impl(ghc >= 9.0) && impl(ghc < 9.2)
-    buildable: False
 
 test-suite unittest
   type: exitcode-stdio-1.0
@@ -245,6 +245,7 @@
   hs-source-dirs:
       test
   ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures 
-Wno-orphans -Wno-type-defaults -Wno-unused-do-bind
+  cpp-options: -DVERSION="1.51.1"
   build-depends:
       Decimal >=0.5.1
     , Glob >=0.9

Reply via email to