imp:journal: use a symlink's target's directory for relative include paths

When reading a symbolically-linked journal file,
relative paths in include directives are now evaluated
relative to the directory of the real linked file,
not the directory of the symlink.

This also seems to fix an obscure case where stats did not report
absolute included file paths in certain circumstances (stdin, maybe no
terminal..)
This commit is contained in:
Simon Michael 2024-02-22 08:16:26 -10:00
parent f6a50923c2
commit 5a36362b33
2 changed files with 16 additions and 12 deletions

View File

@ -104,6 +104,7 @@ import qualified Hledger.Read.CsvReader as CsvReader (reader)
import qualified Hledger.Read.RulesReader as RulesReader (reader)
import qualified Hledger.Read.TimeclockReader as TimeclockReader (reader)
import qualified Hledger.Read.TimedotReader as TimedotReader (reader)
import System.Directory (canonicalizePath)
--- ** doctest setup
-- $setup
@ -304,22 +305,24 @@ includedirectivep = do
where
getFilePaths
:: MonadIO m => Int -> SourcePos -> FilePath -> JournalParser m [FilePath]
getFilePaths parseroff parserpos filename = do
let curdir = takeDirectory (sourceName parserpos)
filename' <- lift $ expandHomePath filename
`orRethrowIOError` (show parserpos ++ " locating " ++ filename)
-- Compiling filename as a glob pattern works even if it is a literal
fileglob <- case tryCompileWith compDefault{errorRecovery=False} filename' of
getFilePaths parseroff parserpos fileglobpattern = do
-- Expand a ~ at the start of the glob pattern, if any.
fileglobpattern' <- lift $ expandHomePath fileglobpattern
`orRethrowIOError` (show parserpos ++ " locating " ++ fileglobpattern)
-- Compile the glob pattern.
fileglob <- case tryCompileWith compDefault{errorRecovery=False} fileglobpattern' of
Right x -> pure x
Left e -> customFailure $
parseErrorAt parseroff $ "Invalid glob pattern: " ++ e
-- Get all matching files in the current working directory, sorting in
-- lexicographic order to simulate the output of 'ls'.
Left e -> customFailure $ parseErrorAt parseroff $ "Invalid glob pattern: " ++ e
-- Get the directory of the including file. This will be used to resolve relative paths.
let parentfilepath = sourceName parserpos
realparentfilepath <- liftIO $ canonicalizePath parentfilepath -- Follow a symlink. If the path is already absolute, the operation never fails.
let curdir = takeDirectory realparentfilepath
-- Find all matched files, in lexicographic order mimicking the output of 'ls'.
filepaths <- liftIO $ sort <$> globDir1 fileglob curdir
if (not . null) filepaths
then pure filepaths
else customFailure $ parseErrorAt parseroff $
"No existing files match pattern: " ++ filename
"No existing files match pattern: " ++ fileglobpattern
parseChild :: MonadIO m => SourcePos -> PrefixedFilePath -> ErroringJournalParser m ()
parseChild parentpos prefixedpath = do

View File

@ -8,4 +8,5 @@ $ hledger -f- stats
include a.j
include b.j
$ touch a.j b.j; hledger -f- stats; rm -f a.j b.j
> /Included files *: *\.\/a\.j/
> /Included files.*\/a\.j
.*\/b\.j/