-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
inconsistent realpath on Windows #30588
Comments
Not sure what the right Windows API function is here. Right now we are calling |
We have an undocumented julia> Base.Filesystem.longpath(uppercase(pwd()))
"C:\\Users\\STEVEN G. JOHNSON\\Desktop\\JULIA-1.0.0"
julia> Base.Filesystem.longpath(lowercase(pwd()))
"c:\\Users\\steven g. johnson\\Desktop\\julia-1.0.0" Weirdly, it seems to canonicalize the case of some portions of the path but not others! (Thank you, Microsoft… 😝) |
Python's See also this question on StackOverflow and also this one, which suggests various solutions, including apparently incorrect suggestions to call |
See also this stackoverflow thread, which says you need to call |
Success! I verified that calling Well, with one exception — it doesn't change the case of the drive letter. 😠 I suppose we can capitalize that manually? ( |
There is this comment on SO that seems to indicate this approach can still fail though https://stackoverflow.com/questions/2113822/python-getting-filename-case-as-stored-in-windows/2114975#comment57405497_2114975, if I'm understanding things correctly. |
Perhaps use |
@vtjnash, to get a file handle, does the file need to have read permission? |
Ah, you can get a handle that you don't have read access to: the CreateFile documentation says that if you pass So getting a handle and calling |
The following seems to do the trick: function myrealpath(path::AbstractString)
h = ccall(:CreateFileW, stdcall, Int, (Cwstring, UInt32, UInt32, Ptr{Cvoid}, UInt32, UInt32, Int),
path, 0, 0x07, C_NULL, 3, 0x02000000, 0)
h == -1 && error(Libc.FormatMessage())
try
len = ccall(:GetFinalPathNameByHandleW, stdcall, UInt32, (Int, Ptr{UInt16}, UInt32, UInt32),
h, C_NULL, 0, 0x0)
iszero(len) && error(Libc.FormatMessage())
buf = Array{UInt16}(undef, len)
len = ccall(:GetFinalPathNameByHandleW, stdcall, UInt32, (Int, Ptr{UInt16}, UInt32, UInt32),
h, buf, len, 0x0)
iszero(len) && error(Libc.FormatMessage())
resize!(buf, len) # strip NUL terminator
if 4 < len < 264 && 0x005c == buf[1] == buf[2] == buf[4] && 0x003f == buf[3]
Base._deletebeg!(buf, 4) # omit \\?\ prefix for paths < MAXPATH in length
end
return transcode(String, buf)
finally
ccall(:CloseHandle, stdcall, Cint, (Int,), h)
end
end |
Bravo 👏🏼 |
On MacOS and Linux,
realpath(p)
throws an error ifp
does not exist, but on Windows there is no error.Furthermore, on case-insensitive case-preserving filesystems (Mac and Windows), arguably
realpath
should canonicalize the case of the path. It does so on MacOS, but not on Windows.(In Python, there is a separate
normcase
function for normalizing case — itsrealpath
function does not normalize case on MacOS, but also does not throw an exception for nonexistent files. My preference would just be to make our Windowsrealpath
act like it does on other operating systems.)(A related problem is determining whether two paths refer to the same file in a portable way. Python has a
samefile
function for this. See also #9436.)The text was updated successfully, but these errors were encountered: