-2

I am on macOS (where file names are case insensitive) and I need to find out the actual case-correct name of a file. For example I do not know whether my file has name A.txt or a.txt or A.TXT or a.TXT etc. I want to display the name correctly on the screen.

What is the fastest way in C/C++?

UPDATE: I tried <filesystem> and Qt framework too (my application is in Qt). But the only possible solution with these libraries seem to be to read full list all entries in the parent directory and then compare if any of these child items matches to the given name pattern. This is obviously not viable for performance reasons.

  • 1
    Someone chose "Needs more focus." I disagree with that sentiment. I would have picked something like "lacks details or clarity" or "needs debugging details." But I've been recently enlightened to the fact that there is no minimum effort required for questions on SO. Have you tried ``? – sweenish Nov 03 '21 at 16:35
  • Oh, yes. I should have mentioned I tried and Qt framework too (my application is in Qt). But the only possible solution with these seem to be to read full list all entries in the parent directory and then compare if any of these does not match to the name pattern. (I am going to update the question) – HiFile.app - best file manager Nov 03 '21 at 16:37
  • If there's no consistent naming it might be worth forcing one by having a little script go through the directory and rename everything to all lower case. – user4581301 Nov 03 '21 at 16:45
  • @user4581301 No, I do not want to change the actual names (they are users' files not mine!). My question is about how do I get the actual case-correct name given I know only for example lowercase name. – HiFile.app - best file manager Nov 03 '21 at 16:48
  • How do you end up with the incorrect case in the first place? – molbdnilo Nov 03 '21 at 16:50
  • @molbdnilo Let's say that user enters name "a.txt" into a dialog. And I want to tell him something like "Beware: There is already file A.txt and if you proceed you will overwrite it." (actually my use case is more complicated, but this is just illustration how I get wrong file name) – HiFile.app - best file manager Nov 03 '21 at 16:53
  • [This](https://en.cppreference.com/w/cpp/filesystem/directory_iterator) gives you what you would need, but the example code does not work on my up-to-date MacBook. It complains about not being able to print the `directory_entry`, but there is supposed to be an `operator<<()` for it according to cppreference.com. I don't know who I would file the bug with. I tested the code in a dev container and it worked fine, but that was Linux. – sweenish Nov 03 '21 at 17:14
  • 1
    Re “macOS (where file names are case insensitive)”: Case sensitivity or insensitivity is a property of the file system, not of macOS. – Eric Postpischil Nov 03 '21 at 20:00
  • @sweenish ... No, this code only can list all items in a directory. Of course I could retrieve case correct name from it but it is obviously suboptimal because listing the whole directory to get a name of just one file is not efficient. – HiFile.app - best file manager Nov 04 '21 at 07:24
  • Get the list and only update it when needed, or do a full search every query? The choice seems pretty obvious. – sweenish Nov 04 '21 at 12:42
  • Although, I suppose you could create a `std::filesystem::path` object and check for existence. Meh. – sweenish Nov 04 '21 at 14:06

1 Answers1

2

std::filesystem::canonical transforms the case of a file to the on disk path (at least on macOS and Visual Studio on Windows, I'm not sure this is a requirement of the standard):

With a file in the current directory called test.cpp the following program prints:

"/Users/user/test.cpp"
#include <filesystem>
#include <iostream>

int main()
{
  std::filesystem::path p("TEST.cpp");
  std::cout << std::filesystem::canonical(p) << "\n";
}

The same program works with absolute paths as well.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
  • Yeah... it is C++17 which I do not have :( – HiFile.app - best file manager Nov 03 '21 at 17:01
  • You said you tried ``. – sweenish Nov 03 '21 at 17:02
  • @sweenish Yes, I tried which is available on my old mac machine. Which is for some obscure reason not C++17... so I do not see canonical in filesystem namespace. And based on documentation, canonical ensures only removing . and .. and symlinks. There is not a word about case correctness. – HiFile.app - best file manager Nov 03 '21 at 17:08
  • it returns a canonical path, on case sensitive file systems that just involves dots and symlinks but on case sensitive filesystems it checks the case too – Alan Birtles Nov 03 '21 at 17:16
  • Thank you for the answer. It probably works but I cannot test it since I do not build with C++17. But I folowed the docs and sourcecode of filesystem::canonical() and it seams it calls realpath() from stdlib.h. I could use this function even without C++17 on my build setup. But it has very unfortunate property of following symlinks which is something that I definitely not want. So my search for proper solution continues... – HiFile.app - best file manager Nov 04 '21 at 07:31
  • I was going to suggest using `F_GETPATH` with a call to `fnctl`, but this looks like an even more readable, modern C++ solution. Do you have any idea if most implementations of the standard library implement `std::filesystem::canonical` in terms of `F_GETPATH`? I would assume so. The advantage of explicitly invoking `F_GETPATH` is that it is guaranteed to work, whereas this may not be. (Win32 has other functions, for example, that canonicalize a path without respect to case.) – Cody Gray - on strike Nov 04 '21 at 07:41
  • Ah, actually... `std::filesystem::canonical` is implemented equivalent to or in terms of `realpath`. (And V.K. already confirmed that, if only I'd read the existing comments first.) That's going to be an issue on macOS, where I don't believe that `realpath` exists, or at least doesn't behave in the desired way. So, `std::filesystem::canonical` is either not going to work properly, or it's going to need to be implemented in terms of `fnctl` with `F_GETPATH` as I initially suggested. You'll need to check your standard library implementation to be sure. – Cody Gray - on strike Nov 04 '21 at 07:47