Move PBD::canonical_path to pbd/file_utils.h/cc and reimplement for Windows
authorTim Mayberry <mojofunk@gmail.com>
Mon, 19 Sep 2016 00:11:59 +0000 (10:11 +1000)
committerTim Mayberry <mojofunk@gmail.com>
Mon, 19 Sep 2016 04:47:52 +0000 (14:47 +1000)
This fixes the libpbd testCanonicalPathUTF8 and libardour
open_session_utf8_path unit tests

You can now have Sessions with localized names containing characters that
aren't in the system codepage on Windows.

It also fixes the issue where a Session would not open when it was moved into a
path with characters that aren't in the system codepage.

The only use case for calling canonical_path/realpath on the session path
AFAICT is for resolving relative paths that are passed via the command
line/terminal. I'm doubtful that works correctly on Windows because of
character encoding issues with the current API we use for that(not glib), so it
is slightly ironic that this issue was caused by an incorrect implementation of
a function that is not really necessary on Windows at this point in time.

libs/pbd/file_utils.cc
libs/pbd/pathexpand.cc
libs/pbd/pbd/file_utils.h

index 26f80f971981965e61ca8f29c3ac4747993504c4..63df6ee3c7dac1def8ba217421cb76a7ad41c2ae 100644 (file)
@@ -361,6 +361,65 @@ get_absolute_path (const std::string & p)
        return Glib::build_filename (Glib::get_current_dir(), p);
 }
 
+string
+canonical_path (const std::string& path)
+{
+#ifdef PLATFORM_WINDOWS
+       wchar_t resolved_wpath[_MAX_PATH];
+
+       // sizeof(wchar_t) is 2 bytes using gcc/mingw and VC++ but 4 bytes using gcc/linux
+       assert (sizeof(wchar_t) == 2);
+
+       wchar_t* wfilepath = (wchar_t*)g_utf8_to_utf16 (path.c_str(), -1, NULL, NULL, NULL);
+
+       if (wfilepath == NULL) {
+               DEBUG_TRACE (
+                   DEBUG::FileUtils,
+                   string_compose ("PBD::canonical_path: Unable to convert path from utf8 to utf16 : %1\n",
+                                   path));
+               return path;
+       }
+
+       if (_wfullpath (resolved_wpath, wfilepath, _MAX_PATH) == NULL) {
+               DEBUG_TRACE (DEBUG::FileUtils,
+                            string_compose ("PBD::canonical_path: Unable to resolve %1\n", wfilepath));
+               return path;
+       }
+
+       gchar* resolved_utf8_path =
+           g_utf16_to_utf8 (reinterpret_cast<const gunichar2*>(resolved_wpath), -1, NULL, NULL, NULL);
+
+       if (resolved_utf8_path == NULL) {
+               DEBUG_TRACE (
+                   DEBUG::FileUtils,
+                   string_compose ("PBD::canonical_path: Unable to convert path from utf16 to utf8 : %1\n",
+                                   resolved_wpath));
+               return path;
+       }
+
+       const string retval(resolved_utf8_path);
+
+       g_free (wfilepath);
+       g_free (resolved_utf8_path);
+
+       return retval;
+
+#else
+       char buf[PATH_MAX+1];
+
+       if (realpath (path.c_str(), buf) == NULL) {
+               DEBUG_TRACE (DEBUG::FileUtils,
+                            string_compose ("PBD::canonical_path: Unable to resolve %1: %2\n", path,
+                                            g_strerror (errno)));
+               return path;
+       }
+       DEBUG_TRACE (DEBUG::FileUtils,
+                    string_compose ("PBD::canonical_path %1 resolved to: %2\n", path, string (buf)));
+
+       return string (buf);
+#endif
+}
+
 std::string
 get_suffix (const std::string & p)
 {
index a92005a08621e9a0d850be26675d53bd3581ca02..63ae348df1f6f7f5da5c5bd05d5d00026bc8bf0e 100644 (file)
 using std::string;
 using std::vector;
 
-#ifdef COMPILER_MINGW
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <glibmm.h>
-
-/****************************************************************
- * Emulate POSIX realpath() using Win32 _fullpath() since realpath()
- * is not available.
- *
- * Returns:
- *    On Success: A pointer to the resolved (absolute) path
- *    On Failure: 0 (NULL)
- */
-
-static char*
-realpath (const char *original_path, char resolved_path[_MAX_PATH+1])
-{
-       char *rpath = 0;
-       bool bIsSymLink = false; // We'll probably need to test the incoming path
-                                // to find out if it points to a Windows shortcut
-                                // (or a hard link) and set this appropriately.
-
-       if (bIsSymLink) {
-               // At the moment I'm not sure if Windows '_fullpath()' is directly
-               // equivalent to POSIX 'realpath()' - in as much as the latter will
-               // resolve the supplied path if it happens to point to a symbolic
-               // link ('_fullpath()' probably DOESN'T do this but I'm not really
-               // sure if Ardour needs such functionality anyway). Therefore we'll
-               // possibly need to add that functionality here at a later date.
-       } else {
-               char temp[(_MAX_PATH+1)*6]; // Allow for maximum length of a path in wchar characters
-
-               // POSIX 'realpath()' requires that the buffer size is at
-               // least PATH_MAX+1, so assume that the user knew this !!
-
-               rpath = _fullpath (temp, Glib::locale_from_utf8 (original_path).c_str(), _MAX_PATH);
-
-               if (0 != rpath) {
-                       snprintf (resolved_path, _MAX_PATH+1, "%s", Glib::locale_to_utf8 (temp).c_str());
-               }
-
-       }
-
-       return (rpath);
-}
-
-#endif  // COMPILER_MINGW
-
-string
-PBD::canonical_path (const std::string& path)
-{
-       char buf[PATH_MAX+1];
-
-       if (realpath (path.c_str(), buf) == NULL) {
-               DEBUG_TRACE (DEBUG::FileUtils,
-                               string_compose("PBD::canonical_path: Unable to resolve %1: %2\n", path, g_strerror(errno)));
-               return path;
-       }
-
-       DEBUG_TRACE (DEBUG::FileUtils,
-                       string_compose("PBD::canonical_path %1 resolved to: %2\n", path, string(buf)));
-
-       return string (buf);
-}
-
 string
 PBD::path_expand (string path)
 {
index e9baaa3e81cd592e9289496400c7f35efd352a86..d44328815903619b0a373e0adee084046ff72f7e 100644 (file)
@@ -195,6 +195,12 @@ LIBPBD_API bool touch_file (const std::string& path);
  */
 LIBPBD_API std::string get_absolute_path (const std::string &);
 
+/**
+ * The equivalent of ::realpath on POSIX systems, on Windows hard
+ * links/junctions etc are not resolved.
+ */
+LIBPBD_API std::string canonical_path (const std::string& path);
+
 /**
  * Take a path/filename and return the suffix (characters beyond the last '.'
  * @return A string containing the suffix, which will be empty