Skip to content

prependCurrentDirectory strips trailing whitespace on Windows #199

@tbidne

Description

@tbidne

Hello,

I encountered the following surprising (to me) behavior. Consider:

{-# LANGUAGE QuasiQuotes #-}

module Main (main)

import System.OsPath (OsPath, osp, (</>))
import System.Directory.Internal qualified as IDir

main :: IO ()
main = putStrLn . show =<< IDir.prependCurrentDirectory relOsPath

-- "a/rel/path   "
relOsPath :: OsPath
relOsPath = [osp|a|] </> [osp|rel|] </> [osp|path   |]

This prints:

"/<root-path>/a/rel/path   " -- unix
"/<root-path>/a/rel/path"    -- windows

This is the latest directory-1.3.9.0 and filepath-1.5.4.0.

I discovered this when a property test expected a filename to be preserved after calling makeAbsolute (which calls prependCurrentDirectory). On unix this test passed, but on windows it failed because the generated path had its trailing whitespace stripped.

My question, is this expected? I don't understand windows paths well enough to know what's going on here. The docs mention that some applications will strip whitespace when actually saving files, but it's not clear to me what part of directory's API is doing this. I attempted to investigate this here, though I was not able to pinpoint an exact cause (I do not have a windows machine, so I am relying on github's CI).

The examples are essentially:

Rel:                            print "a/rel/path   "
Rel normalise:                  print (OsP.normalise "a/rel/path   ")
Rel simplify:                   print (Internal.simplify "a/rel/path   ")

/ </> (root </> a/rel/path   ): print ("/" </> ("root" </> "a/rel/path   "))
/root </> a/rel/path   :        print (("/" </> "root") </> "a/rel/path   ")

Abs:                            print "/a/rel/path   "
Abs normalise:                  print (OsP.normalise "/a/rel/path   ")
Abs simplify:                   print (Internal.simplify "/a/rel/path   ")

Manual prepend:                 print ((</> "a/rel/path   ") <$> Internal.getCurrentDirectoryInternal)
My prepend:                     print (myPrependCurrentDirectory "a/rel/path   ")
Dir prepend:                    print (Internal.prependCurrentDirectory "a/rel/path   ")
Abs:                            print (Dir.makeAbsolute "a/rel/path   ")

And the output is (with links to CI output, though they're probably not publicly available):

-- ubuntu: https://github.com/tbidne/dir-abs-whitespace/actions/runs/13776102934/job/38525559164
Rel:                            "a/rel/path   "
Rel normalise:                  "a/rel/path   "
Rel simplify:                   "a/rel/path   "
                                
/ </> (root </> a/rel/path   ): "/root/a/rel/path   "
/root </> a/rel/path   :        "/root/a/rel/path   "
                                
Abs:                            "/a/rel/path   "
Abs normalise:                  "/a/rel/path   "
Abs simplify:                   "/a/rel/path   "
                                
Manual prepend:                 "/home/runner/work/dir-abs-whitespace/dir-abs-whitespace/a/rel/path   "
My prepend:                     "/home/runner/work/dir-abs-whitespace/dir-abs-whitespace/a/rel/path   "
Dir prepend:                    "/home/runner/work/dir-abs-whitespace/dir-abs-whitespace/a/rel/path   "
Abs:                            "/home/runner/work/dir-abs-whitespace/dir-abs-whitespace/a/rel/path   "
-- windows: https://github.com/tbidne/dir-abs-whitespace/actions/runs/13776102934/job/38525559428
Rel:                            "a\rel\path   "
Rel normalise:                  "a\rel\path   "
Rel simplify:                   "a\rel\path   "
                                
/ </> (root </> a/rel/path   ): "\root\a\rel\path   "
/root </> a/rel/path   :        "\root\a\rel\path   "
                                
Abs:                            "\a\rel\path   "
Abs normalise:                  "\a\rel\path   "
Abs simplify:                   "\a\rel\path   "
                                
Manual prepend:                 "D:\a\dir-abs-whitespace\dir-abs-whitespace\a\rel\path   "
My prepend:                     "D:\a\dir-abs-whitespace\dir-abs-whitespace\a\rel\path   "
Dir prepend:                    "D:\a\dir-abs-whitespace\dir-abs-whitespace\a\rel\path"
Abs:                            "D:\a\dir-abs-whitespace\dir-abs-whitespace\a\rel\path"

Even more bizarre, the "My prepend" example is just directory's Internal.prependCurrentDirectory directly copied, so I'm not sure where the discrepancy is. This code for this is here.

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions