Alternative to realpath for finding parent directories

I’ve seen a couple of things on why using std.fs.realpath is generally bad if you want things to be robust and portable. However, I have a use case that I don’t know how to approach without using realpath.

I’d like to be able to take a relative file path and step through it’s parent, grandparent, etc. directories looking for a config file. For example, assume the config file is at /home/user/project/configfile, the current working directory is at /home/user/project/subfolder/, and the user supplies the relative path src/example.zig. I want to first check /home/user/project/subfolder/src/, then /home/user/project/subfolder/, then find the config file in /home/user/project/.

The only way I can think to do this is by stripping away directories from an absolute path, which requires calling realpath on either the user-provided relative path, or . to get the cwd’s absolute path (which I could use to resolve the relative paths myself).

I do see Andrew Kelly mention that the compiler calls realpath in one place, which looks similar to what I’m trying to do here.

I also assume that hardlinks and symlinks are going to complicate this no matter what, but I don’t know by how much.

  1. Am I overthinking this?
  2. Do the portability and robustness concerns still apply if I only call realpath once to get the cwd’s absolute path?
  3. If the concerns do still apply, is there a less problematic alternative?

The name of the parent folder is "..", so it’s easy to walk up the tree.

Is there a reliable way to know when I’ve hit the root if it doesn’t find the config file? It’s probably necessary to have a maximum number of iterations anyway, but being able to stop as soon as it hits the root would be nice.

A quick test of with the following code doesn’t return an error.

var dir = try std.fs.cwd().openDir("/..", .{});

If this is consistent with cd .. in a shell, then this will just keep returning the root.

1 Like

The Zig compiler actually does something very similar to find the lib directory.

It uses std.fs.path.dirname in a loop with the result of selfExePathAlloc() which is an absolute path. Maybe the result of std.process.getCwd()/getCwdAlloc() + a path.join would work for you?

1 Like

That looks like it’ll work. Thank you! I wasn’t aware of the getCwd() functions in std.process.

1 Like

You can always use stat to get the inode number for /, and compare it to the inode number for your directory, until they match.

Not on Windows…

1 Like

Absolutely right. It is such a relief for me not to ever think about Windows… :slight_smile: