Check if a file is a symlink in Rust 2018 on Windows - windows

I'm writing a small utility for myself that needs to be able to check if a file is a symlink or not.
Using FileType::is_symlink on Windows always returns false (goes for both symlinks to directories, and regular files).
Using regular Rust 2018 edition, is there any way to check if a file is a symlink on Windows?
In my searching, I came across: FileTypeExt - however this requires that you use unstable Rust, as far as I can tell.

Using File::metadata() or fs::metadata() to get the file type will always return false for FileType::is_symlink() because the link has already been followed at that point. The docs note that:
The underlying Metadata struct needs to be retrieved with the fs::symlink_metadata function and not the fs::metadata function. The fs::metadata function follows symbolic links, so is_symlink would always return false for the target file.
use std::fs;
fn main() -> std::io::Result<()> {
let metadata = fs::symlink_metadata("foo.txt")?;
let file_type = metadata.file_type();
let _ = file_type.is_symlink();
Ok(())
}

Related

How to I determine whether two file paths (or file URLs) identify the same file or directory on macOS?

Imagine this simple example of two paths on macOS:
/etc/hosts
/private/etc/hosts
Both point to the same file. But how do you determine that?
Another example:
~/Desktop
/Users/yourname/Desktop
Or what about upper / lower case mixes on a case-insensitive file system:
/Volumes/external/my file
/Volumes/External/My File
And even this:
/Applications/Über.app
Here: The "Ü" can be specified in two unicode composition formats (NFD, NFC). For an example where this can happen when you use the (NS)URL API see this gist of mine.
Since macOS 10.15 (Catalina) there are additionally firmlinks that link from one volume to another in a volume group. Paths for the same FS object could be written as:
/Applications/Find Any File.app
/System/Volumes/Data/Applications/Find Any File.app
I like to document ways that reliably deal with all these intricacies, with the goal of being efficient (i.e. fast).
There are two ways to check if two paths (or their file URLs) point to the same file system item:
Compare their paths. This requires that the paths get prepared first.
Compare their IDs (inodes). This is overall safer as it avoids all the complications with unicode intricacies and wrong case.
Comparing file IDs
In ObjC this is fairly easy (note: Accordingly to a knowledgeable Apple developer one should not rely on [NSURL fileReferenceURL], so this code uses a cleaner way):
NSString *p1 = #"/etc/hosts";
NSString *p2 = #"/private/etc/hosts";
NSURL *url1 = [NSURL fileURLWithPath:p1];
NSURL *url2 = [NSURL fileURLWithPath:p2];
id ref1 = nil, ref2 = nil;
[url1 getResourceValue:&ref1 forKey:NSURLFileResourceIdentifierKey error:nil];
[url2 getResourceValue:&ref2 forKey:NSURLFileResourceIdentifierKey error:nil];
BOOL equal = [ref1 isEqual:ref2];
The equivalent in Swift (note: do not use fileReferenceURL, see this bug report):
let p1 = "/etc/hosts"
let p2 = "/private/etc/hosts"
let url1 = URL(fileURLWithPath: p1)
let url2 = URL(fileURLWithPath: p2)
let ref1 = try url1.resourceValues(forKeys[.fileResourceIdentifierKey])
.fileResourceIdentifier
let ref2 = try url2.resourceValues(forKeys[.fileResourceIdentifierKey])
.fileResourceIdentifier
let equal = ref1?.isEqual(ref2) ?? false
Both solution use the BSD function lstat under the hood, so you could also write this in plain C:
static bool paths_are_equal (const char *p1, const char *p2) {
struct stat stat1, stat2;
int res1 = lstat (p1, &stat1);
int res2 = lstat (p2, &stat2);
return (res1 == 0 && res2 == 0) &&
(stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
}
However, heed the warning about using these kind of file references:
The value of this identifier is not persistent across system restarts.
This is mainly meant for the volume ID, but may also affect the file ID on file systems that do not support persistent file IDs.
Comparing paths
To compare the paths you must get their canonical path first.
If you do not do this, you can not be sure that the case is correct, which in turn will lead to very complex comparison code. (See using NSURLCanonicalPathKey for details.)
There are different ways how the case can be messed up:
The user may have entered the name manually, with the wrong case.
You have previously stored the path but the user has renamed the file's case in the meantime. You path will still identify the same file, but now the case is wrong and a comparison for equal paths could fail depending on how you got the other path you compare with.
Only if you got the path from a file system operation where you could not specify any part of the path incorrectly (i.e. with the wrong case), you do not need to get the canonical path but can just call standardizingPath and then compare their paths for equality (no case-insensitive option necessary).
Otherwise, and to be on the safe side, get the canonical path from a URL like this:
import Foundation
let uncleanPath = "/applications"
let url = URL(fileURLWithPath: uncleanPath)
if let resourceValues = try? url.resourceValues(forKeys: [.canonicalPathKey]),
let resolvedPath = resourceValues.canonicalPath {
print(resolvedPath) // gives "/Applications"
}
If your path is stored in an String instead of a URL object, you could call stringByStandardizingPath (Apple Docs). But that would neither resolve incorrect case nor would it decompose the characters, which may cause problems as shown in the aforementioned gist.
Therefore, it's safer to create a file URL from the String and then use the above method to get the canonical path or, even better, use the lstat() solution to compare the file IDs as shown above.
There's also a BSD function to get the canonical path from a C string: realpath(). However, this is not safe because it does not resolve the case of different paths in a volume group (as shown in the question) to the same string. Therefore, this function should be avoided for this purpose.

Where is 's' defined in https://github.com/Workiva/go-datastructures/tree/master/bitarray?

I do not see where s is defined. Guru will not tell me. All I get is "no object for identifier" but it knows about the k right beside it. Here is a snippet that is typical of the linked code:
func getIndexAndRemainder(k uint64) (uint64, uint64) {
return k / s, k % s
}
The one letter variable name definitely makes it harder to grep around for. I have looked for the usual suspects: var s uint64, s := ..., and nothing. Clearly it needs to be a global value defined somewhere.
This leaves me with two questions:
Where is s coming from?
How would I find it without asking here?
EDIT:
For others who stumble on this.
Guru failed me because I did not checkout the source for the package under a proper Go workspace by placing the git clone under /some/path/src and setting the GOPATH to /some/path. So while I thought GOPATH=. guru definition s would work, the GOPATH was ignored. guru could find k because it is in the file but it did not know how to look in other files.
My grep failed cause const uses a simple = not a :=. I will remember this when grepping in the future.
It is defined in go-datastructures/bitarray/block.go:
// s denotes the size of any element in the block array.
// For a block of uint64, s will be equal to 64
// For a block of uint32, s will be equal to 32
// and so on...
const s = uint64(unsafe.Sizeof(block(0)) * 8)
As the variable s was not defined in the function, and it was not prefixed by a package name or alias, it had to be a global (variable or constant) of the bitarray package.
Once that was known, I went through every file in the folder go-datastructures/bitarray that was not suffixed with _test and I looked for a top-level declaration for s.
It's defined in go-datastructures/bitarray/block.go, line #33:
const s = uint64(unsafe.Sizeof(block(0)) * 8)
"Modern" IDEs with Go support usually have the ability to go to the definition of a symbol / identifier your cursor is at or what you click on. For example in Atom with the Go-plus plugin you can go to the definition by holding down the CTRL key while clicking.
These IDEs use the godef open source tool to find the definition source file and line, you may also use it directly. You can find the godef documentation here: https://godoc.org/github.com/rogpeppe/godef
Another tool, guru is also capable of tracking the definition of it. Both guru and godef works in Atom, and were able to jump to block.go, to the definition of s. But it's much easier to use an "armored" IDE and just do a simple click.
Also note that the success of using grep and patterns is limited, as variable and constant declarations can be grouped, and the following are also valid declarations:
var (
longer = 3
s = uint64(3)
)
Or:
var (
s = someExpression
longer = 3
)
Or:
const (
p = uint64(iota)
s
x
)

wxFileExists returns false in Mac

I am using wxWidgets in Mac with C++ (obviously) and Xcode.
This line returns false, when it should be true.
` if(!wxFileExists(filePicker->GetPath())
{
wxMessageDialog(this, "error").ShowModal();
return;
} `
where filePicker is a wxFilePickerCtrl. It's value is :
filepath std::__1::string "/Users/swtsvn/Main/Proj1/Mac/binaries/osx/Debug/wxSampleApp.app"
The path has upper case, and no spaces. The File is located in that path, since I picked that file path using file picker wxWidget tool.
I searched for a reason wxFileExists might return false on Mac but not on Windows, but could not find one in Google.
If anyone knows the answer, kindly let me know.
According to the docs, wxFileExists():
Returns true if the file exists and is a plain file.
So, it returns false for directories. Mac app bundles, such as your wxSampleApp.app, are directories. It is the intentional behavior of the function that it return false in this case.
You could presumably use wxFileExists(path) || wxDirExists(path) instead, although that will be somewhat inefficient.

Lua: Get function source

I'm working on a system for serialization/deserialization, and I'm trying to get some really fancy stuff going with functions. My goal is to serialize objects containing functions in a human-readable and reversible manner (the serialized files will need processing after the loadfile() step). I need a way to get the actual source of a function, and it looks like I can't always do that with debug.getinfo().
I know that debug.getinfo() will give be the file and line where it was defined (or the source of the function, depending on its origin). Is there a way I can read the function text from that file? I'd be willing to use some kind of parser utilities to do so. Maybe there are Lua packages for parsing Lua code?
Perhaps there's a way I can get loadfile() or require() to automatically retain function source somewhere?
Yes, I know you can get all sorts of information out of debug.getinfo, but it was unable to deal with functions loaded through stdin...
uberblah#glade-m:~$ lua
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> a = function() print("hello, world!") end
> require("serpent")
> s = require("serpent")
> =s.block(debug.getinfo(a))
{
currentline = -1,
func = loadstring("LuaQ\000\000\000\000\000\000\000\000\000=stdin\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000A#\000\000#\000\000�\000\000\000\000\000\000\000\000\000\000\000print\000\000\000\000\000\000\000\000hello, world!\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",'#serialized') --[[function: 0x2068a30]],
lastlinedefined = 1,
linedefined = 1,
namewhat = "",
nups = 0,
short_src = "stdin",
source = "=stdin",
what = "Lua"
} --[[table: 0x206cf80]]
> f = io.open("stdin", "r")
> =f
nil
SOLUTION FOR SOURCE FROM STDIN...
1) Capture all STDIN, write it to a file
2) Load the information from that file, instead of from stdin
3) debug will track the function line numbers in that file
Using debug.getinfo you can get source, linedefined, and lastlinedefined. Unless you format your code really weirdly you should be able to extract the complete code of your function from this info. There is no need to parse code, just to get the correct set of lines.
You want to disasseble the bytecode into Lua statements and expressions. Try http://chunkspy.luaforge.net/. Or maybe http://luadec.luaforge.net/ but I haven't used them so can't give much more info. Luac (Lua compiler) also has -l switch which produces assembly listing that could potentially be parsed. Then there is lbci (bhttp://www.tecgraf.puc-rio.br/~lhf/ftp/lua/#lbci). You might find Getting the AST of a function useful. Finally, I suggest you do a search for "lua decompiler".

boost::filesystem normalize filename

I need normalize file names such that it don't contain any non-portable characters in it. There is portable_file_name but that just checks and returns bool. I need to anyhow convert the given string to a portable name to create files.
Is there any reusable works ?
I assume that you mean some characters (*:;\"?<>/\|) are acceptable as file name or path name characters on some operating systems (Mac OS 9 for instance) but are not acceptable on others (such as Windows XP). Is that correct?
If so, you should probably do the character conversion yourself. I've done this in the past by using a regex to find and replace the unacceptable file name characters with a dash or something that works on all target operating systems. Then, you may safely use the files on both.
Try this:
boost::filesystem3::path portable_file_name;
portable_file_name.normalize();
The best I could come up with so far is:
for (auto &c:name)
{
char test[] = { c,0 };
if (!boost::filesystem::portable_file_name(test))
{
c = '_';
}
}
One of the important steps in doing this is converting file names like ./file or ones pointing to symbolic links into filenames that work on other platforms which might not have these concepts. Boost v1.48.0+ actually has the following functions to do so:
path canonical(const path& p, const path& base = current_path());
path canonical(const path& p, system::error_code& ec);
path canonical(const path& p, const path& base, system::error_code& ec);
This usually involves converting relative paths into absolute ones. These functions are also often called before performing security checks (e.g. is the requested file within the web-root directory of a web server?).
Note that cannonical() requires the file to exist.

Resources