# HG changeset patch
# User Markus Mützel <markus.muetzel@gmx.de>
# Date 1588341024 -7200
#      Fri May 01 15:50:24 2020 +0200
# Branch stable
# Node ID 1d090de50a0d84eb731a5fe922dd84ea91ad353f
# Parent  ae94e3fad6d45780cca585a23cd3cd601112291f
Don't use stat on Windows to check if file exists (bug #57439).

* lo-sysdep.cc, lo-sysdep.h (file_exists, dir_exists): Add new functions
  with system specific code to check if a file or directory exists in
  the file system.

diff -r ae94e3fad6d4 -r 1d090de50a0d libinterp/corefcn/load-path.cc
--- a/libinterp/corefcn/load-path.cc	Fri May 01 00:51:01 2020 -0400
+++ b/libinterp/corefcn/load-path.cc	Fri May 01 15:50:24 2020 +0200
@@ -32,6 +32,7 @@
 #include "dir-ops.h"
 #include "file-ops.h"
 #include "file-stat.h"
+#include "lo-sysdep.h"
 #include "oct-env.h"
 #include "pathsearch.h"
 
@@ -175,9 +176,7 @@
                                  + "private" + sys::file_ops::dir_sep_str ()
                                  + fname;
 
-            sys::file_stat fs (pfname);
-
-            if (fs.exists () && fs.is_reg ())
+            if (sys::file_exists (pfname, false))
               retval = pfname;
           }
       }
@@ -195,30 +194,21 @@
     if (type & load_path::OCT_FILE)
       {
         std::string fnm = nm + ".oct";
-
-        sys::file_stat fs (fnm);
-
-        if (fs.exists () && fs.is_reg ())
+        if (sys::file_exists (fnm, false))
           return fnm;
       }
 
     if (type & load_path::MEX_FILE)
       {
         std::string fnm = nm + ".mex";
-
-        sys::file_stat fs (fnm);
-
-        if (fs.exists () && fs.is_reg ())
+        if (sys::file_exists (fnm, false))
           return fnm;
       }
 
     if (type & load_path::M_FILE)
       {
         std::string fnm = nm + ".m";
-
-        sys::file_stat fs (fnm);
-
-        if (fs.exists () && fs.is_reg ())
+        if (sys::file_exists (fnm, false))
           return fnm;
       }
 
@@ -582,9 +572,7 @@
     if (sys::env::absolute_pathname (file)
         || sys::env::rooted_relative_pathname (file))
       {
-        sys::file_stat fs (file);
-
-        return fs.exists () ? file : retval;
+        return sys::file_exists (file) ? file : retval;
       }
     else
       {
@@ -603,9 +591,7 @@
           {
             std::string tfile = sys::file_ops::concat (di.dir_name, file);
 
-            sys::file_stat fs (tfile);
-
-            if (fs.exists ())
+            if (sys::file_exists (tfile))
               return tfile;
           }
       }
@@ -638,9 +624,7 @@
         && (sys::env::absolute_pathname (dir)
             || sys::env::rooted_relative_pathname (dir)))
       {
-        sys::file_stat fs (dir);
-
-        if (fs.exists () && fs.is_dir ())
+        if (sys::dir_exists (dir))
           return dir;
       }
     else
@@ -665,9 +649,7 @@
                 && sys::file_ops::is_dir_sep (dname[dname_len - dir_len - 1])
                 && canon_dir == dname.substr (dname_len - dir_len))
               {
-                sys::file_stat fs (di.dir_name);
-
-                if (fs.exists () && fs.is_dir ())
+                if (sys::dir_exists (di.dir_name))
                   return di.dir_name;
               }
           }
@@ -685,9 +667,7 @@
         && (sys::env::absolute_pathname (dir)
             || sys::env::rooted_relative_pathname (dir)))
       {
-        sys::file_stat fs (dir);
-
-        if (fs.exists () && fs.is_dir ())
+        if (sys::dir_exists (dir))
           retlist.push_back (dir);
       }
     else
@@ -710,13 +690,9 @@
 
             if (dname_len > dir_len
                 && sys::file_ops::is_dir_sep (dname[dname_len - dir_len - 1])
-                && canon_dir == dname.substr (dname_len - dir_len))
-              {
-                sys::file_stat fs (di.dir_name);
-
-                if (fs.exists () && fs.is_dir ())
-                  retlist.push_back (di.dir_name);
-              }
+                && canon_dir == dname.substr (dname_len - dir_len)
+                && sys::dir_exists (di.dir_name))
+              retlist.push_back (di.dir_name);
           }
       }
 
@@ -746,9 +722,7 @@
             if (sys::env::absolute_pathname (file)
                 || sys::env::rooted_relative_pathname (file))
               {
-                sys::file_stat fs (file);
-
-                if (fs.exists ())
+                if (sys::file_exists (file))
                   return file;
               }
             else
@@ -758,9 +732,7 @@
                     std::string tfile;
                     tfile = sys::file_ops::concat (di.dir_name, file);
 
-                    sys::file_stat fs (tfile);
-
-                    if (fs.exists ())
+                    if (sys::file_exists (tfile))
                       return tfile;
                   }
               }
@@ -823,9 +795,7 @@
             if (sys::env::absolute_pathname (file)
                 || sys::env::rooted_relative_pathname (file))
               {
-                sys::file_stat fs (file);
-
-                if (fs.exists ())
+                if (sys::file_exists (file))
                   retlist.push_back (file);
               }
             else
@@ -835,9 +805,7 @@
                     std::string tfile;
                     tfile = sys::file_ops::concat (di.dir_name, file);
 
-                    sys::file_stat fs (tfile);
-
-                    if (fs.exists ())
+                    if (sys::file_exists (tfile))
                       retlist.push_back (tfile);
                   }
               }
@@ -1007,9 +975,7 @@
 
     std::string file = sys::file_ops::concat (dir, script_file);
 
-    sys::file_stat fs (file);
-
-    if (fs.exists ())
+    if (sys::file_exists (file))
       source_file (file, "base");
   }
 
@@ -1120,11 +1086,11 @@
       move (i, at_end);
     else
       {
-        sys::file_stat fs (dir);
-
-        if (fs)
+        std::string msg = "";
+
+        if (sys::file_exists (dir, true, msg))
           {
-            if (fs.is_dir ())
+            if (sys::dir_exists (dir))
               {
                 dir_info di (dir);
 
@@ -1142,10 +1108,7 @@
               warning ("addpath: %s: not a directory", dir_arg.c_str ());
           }
         else if (warn)
-          {
-            std::string msg = fs.error ();
-            warning ("addpath: %s: %s", dir_arg.c_str (), msg.c_str ());
-          }
+          warning ("addpath: %s: %s", dir_arg.c_str (), msg.c_str ());
       }
 
     // FIXME: is there a better way to do this?
@@ -1464,11 +1427,9 @@
 
         std::string full_name = sys::file_ops::concat (d, fname);
 
-        sys::file_stat fs (full_name);
-
-        if (fs)
+        if (sys::file_exists (full_name))
           {
-            if (fs.is_dir ())
+            if (sys::dir_exists (full_name))
               {
                 if (fname == "private")
                   get_private_file_map (full_name);
@@ -1517,9 +1478,7 @@
 
     std::string pd = sys::file_ops::concat (d, "private");
 
-    sys::file_stat fs (pd);
-
-    if (fs && fs.is_dir ())
+    if (sys::dir_exists (pd))
       method_file_map[class_name].private_file_map = get_fcn_files (pd);
   }
 
@@ -2357,9 +2316,7 @@
               {
                 std::string nm = sys::file_ops::concat (dirname, elt);
 
-                sys::file_stat fs (nm);
-
-                if (fs && fs.is_dir ())
+                if (sys::dir_exists (nm))
                   retval += directory_path::path_sep_str () + genpath (nm, skip);
               }
           }
diff -r ae94e3fad6d4 -r 1d090de50a0d liboctave/system/lo-sysdep.cc
--- a/liboctave/system/lo-sysdep.cc	Fri May 01 00:51:01 2020 -0400
+++ b/liboctave/system/lo-sysdep.cc	Fri May 01 15:50:24 2020 +0200
@@ -31,6 +31,7 @@
 
 #include "dir-ops.h"
 #include "file-ops.h"
+#include "file-stat.h"
 #include "lo-error.h"
 #include "lo-sysdep.h"
 #include "putenv-wrapper.h"
@@ -151,6 +152,126 @@
     }
 
 #if defined (OCTAVE_USE_WINDOWS_API)
+    static std::string
+    get_formatted_last_error ()
+    {
+      std::string msg = "";
+
+      DWORD last_error = GetLastError ();
+
+      wchar_t *error_text = nullptr;
+      FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM |
+                      FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                      FORMAT_MESSAGE_IGNORE_INSERTS,
+                      nullptr, last_error,
+                      MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+                      reinterpret_cast <wchar_t *> (&error_text), 0, nullptr);
+
+      if (error_text != nullptr)
+        {
+          msg = sys::u8_from_wstring (error_text);
+          LocalFree (error_text);
+        }
+      else
+        msg = "Unknown error.";
+
+      return msg;
+    }
+#endif
+
+    bool
+    file_exists (const std::string& filename, bool is_dir)
+    {
+      // Check if a file with that name exists on the file system.  If is_dir
+      // is_dir is true (the default), also return true if filename refers to a
+      // directory.
+#if defined (OCTAVE_USE_WINDOWS_API)
+      std::wstring w_fn = octave::sys::u8_to_wstring (filename);
+
+      DWORD f_attr = GetFileAttributesW (w_fn.c_str ());
+
+      return (f_attr != INVALID_FILE_ATTRIBUTES
+              && (is_dir || ! (f_attr & FILE_ATTRIBUTE_DIRECTORY)));
+#else
+      octave::sys::file_stat fs (filename);
+
+      return (fs && (fs.is_reg () || (is_dir && fs.is_dir ())));
+
+#endif
+    }
+
+    bool
+    file_exists (const std::string& filename, bool is_dir, std::string& msg)
+    {
+      // Check if a file with that name exists on the file system.  If is_dir
+      // is_dir is true (the default), also return true if filename refers to a
+      // directory.
+#if defined (OCTAVE_USE_WINDOWS_API)
+      std::wstring w_fn = octave::sys::u8_to_wstring (filename);
+
+      DWORD f_attr = GetFileAttributesW (w_fn.c_str ());
+
+      if (f_attr == INVALID_FILE_ATTRIBUTES)
+        msg = get_formatted_last_error ();
+
+      return (f_attr != INVALID_FILE_ATTRIBUTES
+              && (is_dir || ! (f_attr & FILE_ATTRIBUTE_DIRECTORY)));
+#else
+      octave::sys::file_stat fs (filename);
+
+      if (! fs)
+        msg = fs.error ();
+
+      return (fs && (fs.is_reg () || (is_dir && fs.is_dir ())));
+
+#endif
+    }
+
+    bool
+    dir_exists (const std::string& dirname)
+    {
+      // Check if a directory with that name exists on the file system.
+#if defined (OCTAVE_USE_WINDOWS_API)
+      std::wstring w_dn = octave::sys::u8_to_wstring (dirname);
+
+      DWORD f_attr = GetFileAttributesW (w_dn.c_str ());
+
+      return (f_attr != INVALID_FILE_ATTRIBUTES
+              && (f_attr & FILE_ATTRIBUTE_DIRECTORY));
+#else
+      octave::sys::file_stat fs (dirname);
+
+      return (fs && fs.is_dir ());
+
+#endif
+    }
+
+    bool
+    dir_exists (const std::string& dirname, std::string& msg)
+    {
+      // Check if a directory with that name exists on the file system.
+#if defined (OCTAVE_USE_WINDOWS_API)
+      std::wstring w_dn = octave::sys::u8_to_wstring (dirname);
+
+      DWORD f_attr = GetFileAttributesW (w_dn.c_str ());
+
+      if (f_attr == INVALID_FILE_ATTRIBUTES)
+        msg = get_formatted_last_error ();
+
+      return (f_attr != INVALID_FILE_ATTRIBUTES
+              && (f_attr & FILE_ATTRIBUTE_DIRECTORY));
+#else
+      octave::sys::file_stat fs (dirname);
+
+      if (! fs)
+        msg = fs.error ();
+
+      return (fs && fs.is_dir ());
+
+#endif
+    }
+
+#if defined (OCTAVE_USE_WINDOWS_API)
 
     static bool check_fseek_ftell_workaround_needed (bool set_nonbuffered_mode)
     {
diff -r ae94e3fad6d4 -r 1d090de50a0d liboctave/system/lo-sysdep.h
--- a/liboctave/system/lo-sysdep.h	Fri May 01 00:51:01 2020 -0400
+++ b/liboctave/system/lo-sysdep.h	Fri May 01 15:50:24 2020 +0200
@@ -47,6 +47,15 @@
     extern bool get_dirlist (const std::string& dirname, string_vector& dirlist,
                              std::string& msg);
 
+    extern bool file_exists (const std::string& filename, bool is_dir = true);
+
+    extern bool file_exists (const std::string& filename, bool is_dir,
+                             std::string& msg);
+
+    extern bool dir_exists (const std::string& dirname);
+
+    extern bool dir_exists (const std::string& dirname, std::string& msg);
+
     extern std::FILE * fopen (const std::string& name, const std::string& mode);
 
     extern void putenv_wrapper (const std::string& name,
