diff --git a/src/HiddenFiles.jl b/src/HiddenFiles.jl index 0bfe883..33ca853 100644 --- a/src/HiddenFiles.jl +++ b/src/HiddenFiles.jl @@ -58,9 +58,9 @@ include("docs.jl") # https://github.com/dotnet/runtime/blob/5992145db2cb57956ee444aa0f0c2f3f85ee3673/src/native/libs/System.Native/pal_io.c#L219 # https://github.com/davidkaya/corefx/blob/4fd3d39f831f3e14f311b0cdc0a33d662e684a9c/src/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs#L88 - _isinvisible(f::AbstractString) = (_st_flags(f) & UF_HIDDEN) == UF_HIDDEN + _isinvisible_st_flags(f::AbstractString) = (_st_flags(f) & UF_HIDDEN) == UF_HIDDEN - _ishidden_bsd_related(f::AbstractString) = _ishidden_unix(f) || _isinvisible(f) + _ishidden_bsd_related(f::AbstractString) = _ishidden_unix(f) || _isinvisible_st_flags(f) end @static if Sys.isapple() # macOS/Darwin @@ -86,15 +86,24 @@ include("docs.jl") # - `/tmp`—Contains temporary files created by apps and the system. # - `/usr`—Contains non-essential command-line binaries, libraries, header files, and other data. # - `/var`—Contains log files and other files whose content is variable. (Log files are typically viewed using the Console app.) - # TODO - _issystemfile(f::AbstractString) = false + _issystemdir(f::AbstractString) = false # TODO + + # This _isinvisible function seems to capture some cases (e.g., `/tmp`) that the other _isinvisible function does not + function _isinvisible_macos_item_info(f::AbstractString, str_encoding::Unsigned = CF_STRING_ENCODING, path_style::Integer = K_CF_URL_POSIX_PATH_STYLE) + cfstr = _cfstring_create_with_cstring(f, str_encoding) + url_ref = _cf_url_create_with_file_system_path(cfstr, isdir(f)) + item_info = _ls_copy_item_info_for_url(url_ref, K_IS_INVISIBLE) + return !iszero(item_info[1] & K_IS_INVISIBLE) + end #=== Case 3: Explicitly hidden files and directories ===# # The Finder may hide specific files or directories that should not be accessed directly by the user. The most notable example of # this is the /Volumes directory, which contains a subdirectory for each mounted disk in the local file system from the command line. # (The Finder provides a different user interface for accessing local disks.) In macOS 10.7 and later, the Finder also hides the - # `~/Library` directory—that is, the `Library` directory located in the user’s home directory. This case is handled by `_isinvisible`. + # `~/Library` directory—that is, the `Library` directory located in the user’s home directory. + # + # This case is handled by `_isinvisible_st_flags`. #=== Case 4: Packages and bundles ===# @@ -149,7 +158,7 @@ include("docs.jl") #=== All macOS cases ===# - _ishidden_macos(f::AbstractString) = _ishidden_bsd_related(f) || _issystemfile(f) || _exists_inside_package_or_bundle(f) + _ishidden_macos(f::AbstractString) = _ishidden_bsd_related(f) || _issystemdir(f) || _isinvisible_macos_item_info(f) || _exists_inside_package_or_bundle(f) _ishidden = _ishidden_macos elseif Sys.isbsd() # BSD; this excludes macOS through control flow (as macOS is checked for first) _ishidden_bsd(f::AbstractString) = _ishidden_bsd_related(f) diff --git a/src/utils/darwin.jl b/src/utils/darwin.jl index 3a2c940..ca07321 100644 --- a/src/utils/darwin.jl +++ b/src/utils/darwin.jl @@ -196,3 +196,51 @@ function _string_from_cf_string(cfstr::Cstring, encoding::Unsigned = CF_STRING_E return String(take!(cfio)) end +#===============================================# + +# https://developer.apple.com/documentation/coreservices/lsiteminfoflags/klsiteminfoisinvisible +# TODO: convert this to enum: https://developer.apple.com/documentation/coreservices/lsiteminfoflags: https://github.com/phracker/MacOSX-SDKs/blob/041600eda65c6a668f66cb7d56b7d1da3e8bcc93/MacOSX10.6.sdk/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Headers/LSInfo.h#L95-L111 +const KLS_ITEM_INFO_IS_INVISIBLE = 0x00000040 + +# https://developer.apple.com/documentation/coreservices/1429609-anonymous/kisinvisible +const K_IS_INVISIBLE = 0x00000040 + +# https://developer.apple.com/documentation/corefoundation/cfurlpathstyle +const K_CF_URL_POSIX_PATH_STYLE = zero(Int8) + +# https://developer.apple.com/documentation/corefoundation/1543250-cfurlcreatewithfilesystempath +function _cf_url_create_with_file_system_path(cfstr::Cstring, is_directory::Bool, path_style::Integer = K_CF_URL_POSIX_PATH_STYLE) + # TODO: handle error codes + # CFURLRef CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle pathStyle, Boolean isDirectory); + url_ref = ccall(:CFURLCreateWithFileSystemPath, Ptr{UInt32}, + (Ptr{Cvoid}, Cstring, Int32, Bool), + C_NULL, cfstr, path_style, is_directory) + return url_ref +end + +# https://developer.apple.com/documentation/coreservices/lsiteminforecord +function _ls_item_info_record() + @warn "LSItemInfoRecord has been deprecated since macOS 10.11" + error("not yet implemented") +end + +# https://developer.apple.com/documentation/coreservices/lsrequestedinfo/klsrequestallflags +const K_LS_REQUEST_ALL_FLAGS = 0x00000010 +# TODO: convert this to enum: https://developer.apple.com/documentation/coreservices/lsrequestedinfo: https://github.com/phracker/MacOSX-SDKs/blob/041600eda65c6a668f66cb7d56b7d1da3e8bcc93/MacOSX10.6.sdk/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Headers/LSInfo.h#L83-L93 + +# https://developer.apple.com/documentation/coreservices/1445685-lscopyiteminfoforurl +function _ls_copy_item_info_for_url(url_ref::Ptr{UInt32}, requested_info::Unsigned = K_LS_REQUEST_ALL_FLAGS) + # TODO: handle error codes + requested_info == K_LS_REQUEST_ALL_FLAGS && @warn "kLSRequestAllFlags has been deprecated since macOS 10.11" + requested_info == KLS_ITEM_INFO_IS_INVISIBLE && @warn "kLSItemInfoIsInvisible has been deprecated since macOS 10.11; ensure you are using kIsInvisible instead" + @warn "LSCopyItemInfoForURL has been deprecated since macOS 10.11" + # buf = Vector{UInt32}(undef, 100) + buf = zeros(UInt32, 100) + # OSStatus LSCopyItemInfoForURL(CFURLRef inURL, LSRequestedInfo inWhichInfo, LSItemInfoRecord *outItemInfo); + ptr = ccall(:LSCopyItemInfoForURL, Ptr{UInt32}, + (Ptr{UInt32}, UInt32, Ptr{Cvoid}), + url_ref, requested_info, buf) + return buf +end + + diff --git a/test/runtests.jl b/test/runtests.jl index 427de09..4d7009f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -25,15 +25,30 @@ using Test # Case 2: UNIX-specific directories # TODO: complete this case - @test HiddenFiles.ishidden("/bin/") - @test HiddenFiles.ishidden("/dev/") - @test HiddenFiles.ishidden("/usr/") - @test !HiddenFiles.ishidden("/tmp/") - + @test HiddenFiles.ishidden("/bin") + @test HiddenFiles.ishidden("/dev") + @test HiddenFiles.ishidden("/dev") + @test HiddenFiles.ishidden("/etc") + @test HiddenFiles.ishidden("/sbin") + @test HiddenFiles.ishidden("/sbin") + @test HiddenFiles.ishidden("/tmp") + @test HiddenFiles.ishidden("/usr") + @test HiddenFiles.ishidden("/var") + @test HiddenFiles._isinvisible_macos_item_info("/bin") + @test HiddenFiles._isinvisible_macos_item_info("/dev") + @test HiddenFiles._isinvisible_macos_item_info("/dev") + @test HiddenFiles._isinvisible_macos_item_info("/etc") + @test HiddenFiles._isinvisible_macos_item_info("/sbin") + @test HiddenFiles._isinvisible_macos_item_info("/sbin") + @test HiddenFiles._isinvisible_macos_item_info("/tmp") + @test HiddenFiles._isinvisible_macos_item_info("/usr") + @test HiddenFiles._isinvisible_macos_item_info("/var") + @test !HiddenFiles._isinvisible_macos_item_info("/bin/bash") + # Case 3: Explicitly hidden files and directories - @test HiddenFiles._isinvisible("/Volumes") + @test HiddenFiles._isinvisible_st_flags("/Volumes") @test ishidden("/Volumes") - @test !HiddenFiles._isinvisible(p′) + @test !HiddenFiles._isinvisible_st_flags(p′) # Case 4: Packages and bundles @test !ishidden("/System/Applications/Utilities/Terminal.app") @@ -41,7 +56,8 @@ using Test @test ishidden("/System/Applications/Utilities/Terminal.app/Contents/") # This should be the same as above, as we expand all paths using realpath @test !HiddenFiles._ispackage_or_bundle("/System/Applications/Utilities/Terminal.app/Contents/") @test HiddenFiles._exists_inside_package_or_bundle("/System/Applications/Utilities/Terminal.app/Contents/") - @test !HiddenFiles._exists_inside_package_or_bundle("/bin/") + @test !HiddenFiles._exists_inside_package_or_bundle("/bin") + @test !HiddenFiles._exists_inside_package_or_bundle("/tmp") f = String(rand(Char, 32)) # this path shouldn't exist cfstr_nonexistent = HiddenFiles._cfstring_create_with_cstring(f) @test_throws Exception HiddenFiles._mditem_create(cfstr_nonexistent) @@ -56,9 +72,9 @@ using Test # TODO: should we not only support FreeBSD? Are we testing on other BSD systems? OpenBSD? @testset "HiddenFiles.jl—FreeBSD" begin @test ishidden(p) - @test !HiddenFiles._isinvisible(p) + @test !HiddenFiles._isinvisible_st_flags(p) @test ishidden(p′) - @test !HiddenFiles._isinvisible(p′) + @test !HiddenFiles._isinvisible_st_flags(p′) @test !ishidden(homedir()) @test !ishidden("/bin/") @test !ishidden("/dev/")