On 12/09/15 12:07 -0600, Martin Sebor wrote: >On 09/12/2015 04:09 AM, Jonathan Wakely wrote: >>On 11 September 2015 at 18:39, Martin Sebor wrote: >>>On 09/11/2015 08:21 AM, Jonathan Wakely wrote: >>>> >>>>Solaris 10 doesn't follow POSIX in accepting a null pointer as the >>>>second argument to realpath(), so allocate a buffer for it. >>> >>> >>>FWIW, the NULL requirement is new in Issue 7. In Issue 6, the behavior >>>is implementation-defined, and before then it was an error. Solaris 10 >>>claims conformance to SUSv2 and its realpath fails with EINVAL. >>>Solaris 11 says it conforms to Issue 6 but according to the man page >>>its realpath already implements the Issue 7 requirement. >> >>Thanks. >> >>>I suspect the same problem will come up on other systems so checking >>>the POSIX version might be better than testing for each OS. >> >>The problem with doing that is that many BSD systems have supported >>passing NULL as an extension long before issue 7, so if we just check >>something like _POSIX_VERSION >= 200809L then we can only canonicalize >>paths up to PATH_MAX on many systems where the extension is available >>but _POSIX_VERSION implies conformance to an older standard. > >You're right. I agree that just checking the POSIX version may not >lead to optimal results. Some implementations also support multiple >versions and the one in effect may not be the one most recent. To >get the most out of those, it's usually recommended to set >_POSIX_C_SOURCE to the latest version before including any headers, >then test the supported version, and when the supported version is >less than the one requested and involves implementation defined >behavior (as in Issue 6) or undefined behavior that's known to be >used to provide extensions (as in SUSv2), check the implementation >version just as the patch does. > >> >>So maybe we want an autoconf macro saying whether realpath() accepts >>NULL, and just hardcode it for the targets known to support it, and >>only check _POSIX_VERSION for the unknowns. > >That will work for Issue 6 where the realpath behavior is >implementation-defined. The test wouldn't yield reliable results >for SUSv2 implementations where the behavior is undefined. There, >the result would have to be hardcoded based on what the manual says. >An autoconf test won't help with the ENAMETOOLONG problem since it >might depend on the filesystem. To overcome that, libstdc++ would >need to do the path traversal itself. It turns out I was wrong about BSD traditionally supporting realpath(path, NULL), it first appeared in these relatively recent versions: FreeBSD 9.0 OpenBSD 5.0 NetBSD 6.1 Like Solaris 11, some of them still define _POSIX_VERSION=200112L even though they support passing NULL, so we could hardcode the targets that are known to support it, but we'll still need a fallback for lots of slightly older targets anyway. So here's a new implementation of canonical() which only uses realpath() when this autoconf compile-or-link test passes: #if _XOPEN_VERSION < 500 #error #elif _XOPEN_VERSION >= 700 || defined(PATH_MAX) char *tmp = realpath((const char*)NULL, (char*)NULL); #else #error #endif i.e. I use realpath() for Issue 7, or for Issue 6 if PATH_MAX is defined. Then in filesystem::canonical() if _GLIBCXX_USE_REALPATH is set I use it, passing NULL for Issue 7, or allocating a buffer of PATH_MAX otherwise. If realpath isn't supported or fails with ENAMETOOLONG then I do the path traversal by hand (which can be done entirely using the std::experimental::filesystem operations). Only passing NULL for Issue 7 is quite conservative. It means we don't do it for targets that support it as an implementation-defined extension to Issue 6, which includes Solaris 11, the BSDs and even older GNU systems (including RHEL6). But that's OK, we have a fallback now so it means no loss of functionality, just efficiency. We can tweak the config later for targets known to handle NULL. The new testcase is not very thorough. I've run a few more involved tests that aren't suitable to check in until I figure out a good way of running filesystem tests that can create/remove arbitrary files and symlinks. What do you think? Tested powerpc64le-linux and x86_64-dragonfly4.2.