0

I'm working with C++17 on Windows. As far as I can see, std::filesystem doesn't have an is_root() function or something similar that tells me if a path refers directly to C: or D: or any other volumen. Am I missing something?

Currently I'm doing this:

if (path.parent_path() == path)
{
    //
}

It looks like it's working, but I don't know if this misses any edge cases. Is there a better way?

EDIT:

I want to check if the entirety of the path is just the volume name (maybe followed by an optional slash or backslash).

So, if there was a function like this, I'd like it to behave as follows:

namespace fs = std::filesystem;
is_root(fs::path{ "C:" }); // true
is_root(fs::path{ "D:\\"}); // true
is_root(fs::path{ "C:/users" }) // false
is_root(fs::current_path()) // usually false, unless the executable was started directly in C: or D: or any other drive
sebrockm
  • 5,733
  • 2
  • 16
  • 39
  • Are you asking if a path has a volume name in it, or if the entirety of the path is *just* a volume name? – Nicol Bolas Apr 17 '20 at 14:23
  • @NicolBolas the entirety of the path shall be *just* that volumen name. I will clarify that in my question. Thanks – sebrockm Apr 17 '20 at 14:24
  • Does [this](https://stackoverflow.com/questions/8518743/get-directory-from-file-path-c) answer the question? Obviously use find instead of rfind. – Cortex0101 Apr 17 '20 at 14:27
  • @Cortex not really. I want to know if my path **is** the volume (the root of it), not if it contains a volume. – sebrockm Apr 17 '20 at 14:28
  • Yes but if the filepath does not contain // then it is the volume yes? – Cortex0101 Apr 17 '20 at 14:30
  • 2
    I don't have a windows installation right here, but wouldn't it be better to use [std::filesystem::path::root_path](https://en.cppreference.com/w/cpp/filesystem/path/root_path) on `path` and then check if they are equal? – t.niese Apr 17 '20 at 14:31
  • @t.niese I was thinking about that, too. But I'm not sure, hence I asked this question – sebrockm Apr 17 '20 at 14:33
  • @Cortex the function/algorithm I'm looking for shall return `true` on `fs::path{"C:"}` as well as on `fs::path{"C:/"}` as well as on `fs::path{"C:\\"}` – sebrockm Apr 17 '20 at 14:35
  • @sebrockm: That's a bit more complicated than what you seemed to ask for in your question. Those latter two paths also have a root directory in them; they don't just specify the volume. You really should put that in the question. – Nicol Bolas Apr 17 '20 at 14:36
  • @NicolBolas Thanks, I thought that with or without (back)slash this either way refers to the root of a drive. Added that to the question – sebrockm Apr 17 '20 at 14:42

1 Answers1

1

First, check to see if it has_root_name(); without one, it obviously doesn't specify a volume.

The hard part is to figure out if it only has a root-name. That's complicated because you also want to ignore the root-directory of the path if it specifies one.

The iterator range is a good, performance-friendly solution here. If it has a root-name, then begin() points to that root-name. So if you increment it, it points to the next component in the path.

If the path has both a root-name and a root-directory, then the component after the root-name is the root-directory. So if you increment past that, the iterator either is pointing to an additional component or it has reached the end of the range. And if it's at the end, then there's nothing left and you know that the path is "just a volume".

The code would look like this:

bool is_volume(const fs::path &p)
{
  if(!p.has_root_name()) return false;

  auto it = p.begin();
  ++it; //Skip the root-name

  if(p.has_root_directory())
    ++it; //Skip the root-directory.

  if(it == p.end())
    return true;

  return false;
}
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • I was not aware that the slash behind the root name matters. I always thought it was optional. Then I need to clarify if for my task if I really need to cover "C:" as well. Either way, this looks good. I will test it. – sebrockm Apr 17 '20 at 15:05
  • @sebrockm: The path "c:" specifies the volume "c:". The path "c:\" specifies the *root directory* of the volume "c:". Each volume has its own current directory; the path "c:name" specifies the "name" file/directory in the current directory of the "c:" volume. By contrast, "c:\name" specifies the "name" file/directory in the root directory of the "c:" volume. – Nicol Bolas Apr 17 '20 at 15:07
  • Thanks for the clarification! – sebrockm Apr 17 '20 at 15:09
  • Note that drive "X:" is implemented as a link in the object namespace. The link usually targets a volume device, but it may target an arbitrary path on a local or remote device (e.g. a subst or mapped drive). So not all root paths on a DOS drive, e.g. "X:\", are actually volume mountpoints. – Eryk Sun Apr 18 '20 at 11:39
  • Also, a volume device has other usable names, such as "\\?\BootPartition" or "\\?\Volume{GUID}", set via `DefineDosDeviceW`. So some paths without a drive-letter name are actually volume mountpoint paths, e.g. "\\?\BootPartition\". – Eryk Sun Apr 18 '20 at 11:43
  • Also, a filesystem directory may be a junction to a local device path. If it targets a filesystem directory, then it's a mountpoint, and if it's the root path of a reserved "\\?\Volume{GUID}" name, then it's a volume mountpoint. Thus "C:\Mount\BackupDrive" may also be a volume mountpoint path. – Eryk Sun Apr 18 '20 at 11:44