changeset 23744:ae74608b6a5d

don't use singleton pattern for file_ops * file-ops.h, file-ops.cc: Don't use singleton. Convert file_ops class to namespace.
author John W. Eaton <jwe@octave.org>
date Fri, 07 Jul 2017 08:17:32 -0400
parents e919cc8d9d92
children a732be902061
files liboctave/system/file-ops.cc liboctave/system/file-ops.h
diffstat 2 files changed, 386 insertions(+), 458 deletions(-) [+]
line wrap: on
line diff
--- a/liboctave/system/file-ops.cc	Fri Jul 07 07:21:20 2017 -0400
+++ b/liboctave/system/file-ops.cc	Fri Jul 07 08:17:32 2017 -0400
@@ -51,367 +51,356 @@
 #include "str-vec.h"
 #include "unistd-wrappers.h"
 
+// The following tilde-expansion code was stolen and adapted from
+// readline.
+
+// The default value of tilde_additional_prefixes.  This is set to
+// whitespace preceding a tilde so that simple programs which do not
+// perform any word separation get desired behavior.
+static const char *default_prefixes[] = { " ~", "\t~", ":~", 0 };
+
+// The default value of tilde_additional_suffixes.  This is set to
+// whitespace or newline so that simple programs which do not perform
+// any word separation get desired behavior.
+static const char *default_suffixes[] = { " ", "\n", ":", 0 };
+
+static size_t
+tilde_find_prefix (const std::string& s, size_t& len)
+{
+  len = 0;
+
+  size_t s_len = s.length ();
+
+  if (s_len == 0 || s[0] == '~')
+    return 0;
+
+  string_vector prefixes = octave::sys::file_ops::tilde_additional_prefixes;
+
+  if (! prefixes.empty ())
+    {
+      for (size_t i = 0; i < s_len; i++)
+        {
+          for (int j = 0; j < prefixes.numel (); j++)
+            {
+              size_t pfx_len = prefixes[j].length ();
+
+              if (prefixes[j] == s.substr (i, pfx_len))
+                {
+                  len = pfx_len - 1;
+                  return i + len;
+                }
+            }
+        }
+    }
+
+  return s_len;
+}
+
+// Find the end of a tilde expansion in S, and return the index
+// of the character which ends the tilde definition.
+
+static size_t
+tilde_find_suffix (const std::string& s)
+{
+  size_t s_len = s.length ();
+
+  string_vector suffixes = octave::sys::file_ops::tilde_additional_suffixes;
+
+  size_t i = 0;
+
+  for ( ; i < s_len; i++)
+    {
+      if (octave::sys::file_ops::is_dir_sep (s[i]))
+        break;
+
+      if (! suffixes.empty ())
+        {
+          for (int j = 0; j < suffixes.numel (); j++)
+            {
+              size_t sfx_len = suffixes[j].length ();
+
+              if (suffixes[j] == s.substr (i, sfx_len))
+                return i;
+            }
+        }
+    }
+
+  return i;
+}
+
+// Take FNAME and return the tilde prefix we want expanded.
+
+static std::string
+isolate_tilde_prefix (const std::string& fname)
+{
+  size_t f_len = fname.length ();
+
+  size_t len = 1;
+
+  while (len < f_len && ! octave::sys::file_ops::is_dir_sep (fname[len]))
+    len++;
+
+  return fname.substr (1, len);
+}
+
+// Do the work of tilde expansion on FILENAME.  FILENAME starts with a
+// tilde.
+
+static std::string
+tilde_expand_word (const std::string& filename)
+{
+  size_t f_len = filename.length ();
+
+  if (f_len == 0 || filename[0] != '~')
+    return std::string (filename);
+
+  // A leading '~/' or a bare '~' is *always* translated to the value
+  // of $HOME or the home directory of the current user, regardless of
+  // any preexpansion hook.
+
+  if (f_len == 1 || octave::sys::file_ops::is_dir_sep (filename[1]))
+    return octave::sys::env::get_home_directory () + filename.substr (1);
+
+  std::string username = isolate_tilde_prefix (filename);
+
+  size_t user_len = username.length ();
+
+  std::string dirname;
+
+  if (octave::sys::file_ops::tilde_expansion_preexpansion_hook)
+    {
+      std::string expansion
+        = octave::sys::file_ops::tilde_expansion_preexpansion_hook (username);
+
+      if (! expansion.empty ())
+        return expansion + filename.substr (user_len+1);
+    }
+
+  // No preexpansion hook, or the preexpansion hook failed.  Look in the
+  // password database.
+
+  octave::sys::password pw = octave::sys::password::getpwnam (username);
+
+  if (! pw)
+    {
+      // If the calling program has a special syntax for expanding tildes,
+      // and we couldn't find a standard expansion, then let them try.
+
+      if (octave::sys::file_ops::tilde_expansion_failure_hook)
+        {
+          std::string expansion
+            = octave::sys::file_ops::tilde_expansion_failure_hook (username);
+
+          if (! expansion.empty ())
+            dirname = expansion + filename.substr (user_len+1);
+        }
+
+      // If we don't have a failure hook, or if the failure hook did not
+      // expand the tilde, return a copy of what we were passed.
+
+      if (dirname.empty ())
+        dirname = filename;
+    }
+  else
+    dirname = pw.dir () + filename.substr (user_len+1);
+
+  return dirname;
+}
+
 namespace octave
 {
   namespace sys
   {
-    file_ops *sys::file_ops::instance = nullptr;
-
-    bool
-    sys::file_ops::instance_ok (void)
+    namespace file_ops
     {
-      bool retval = true;
+      char dev_sep_char (void)
+      {
+#if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
+        return ':';
+#else
+        return 0;
+#endif
+      }
 
-      if (! instance)
-        {
+      char dir_sep_char (void)
+      {
 #if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
-          char system_dev_sep_char = ':';
-          char system_dir_sep_char = '\\';
-          std::string system_dir_sep_str = "\\";
+        return '\\';
 #else
-          char system_dev_sep_char = 0;
-          char system_dir_sep_char = '/';
-          std::string system_dir_sep_str = "/";
+        return '/';
 #endif
+      }
+
+      std::string dir_sep_str (void)
+      {
+#if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
+        return "\\";
+#else
+        return "/";
+#endif
+      }
+
+      std::string dir_sep_chars (void)
+      {
 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM)
-          std::string system_dir_sep_chars = "/\\";
+        return "/\\";
 #else
-          std::string system_dir_sep_chars = system_dir_sep_str;
+        return dir_sep_str ();
 #endif
-
-          instance = new file_ops (system_dev_sep_char, system_dir_sep_char,
-                                   system_dir_sep_str, system_dir_sep_chars);
+      }
 
-          if (instance)
-            singleton_cleanup_list::add (cleanup_instance);
-        }
+      tilde_expansion_hook tilde_expansion_preexpansion_hook = 0;
 
-      if (! instance)
-        (*current_liboctave_error_handler)
-          ("unable to create file_ops object!");
+      tilde_expansion_hook tilde_expansion_failure_hook = 0;
 
-      return retval;
-    }
+      string_vector tilde_additional_prefixes = default_prefixes;
 
-    // The following tilde-expansion code was stolen and adapted from
-    // readline.
+      string_vector tilde_additional_suffixes = default_suffixes;
+
+      bool is_dev_sep (char c)
+      {
+#if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
+        return c == dev_sep_char ();
+#else
+        octave_unused_parameter (c);
 
-    // The default value of tilde_additional_prefixes.  This is set to
-    // whitespace preceding a tilde so that simple programs which do not
-    // perform any word separation get desired behavior.
-    static const char *default_prefixes[] = { " ~", "\t~", ":~", 0 };
+        return false;
+#endif
+      }
 
-    // The default value of tilde_additional_suffixes.  This is set to
-    // whitespace or newline so that simple programs which do not perform
-    // any word separation get desired behavior.
-    static const char *default_suffixes[] = { " ", "\n", ":", 0 };
+      bool is_dir_sep (char c)
+      {
+        std::string tmp = dir_sep_chars ();
+        return tmp.find (c) != std::string::npos;
+      }
 
-    // If non-null, this contains the address of a function that the
-    // application wants called before trying the standard tilde
-    // expansions.  The function is called with the text sans tilde, and
-    // returns a malloc()'ed string which is the expansion, or a NULL
-    // pointer if the expansion fails.
-    sys::file_ops::tilde_expansion_hook
-      sys::file_ops::tilde_expansion_preexpansion_hook = 0;
+      std::string tilde_expand (const std::string& name)
+      {
+        if (name.find ('~') == std::string::npos)
+          return std::string (name);
+        else
+          {
+            std::string result;
+
+            size_t name_len = name.length ();
+
+            // Scan through S expanding tildes as we come to them.
 
-    // If non-null, this contains the address of a function to call if the
-    // standard meaning for expanding a tilde fails.  The function is
-    // called with the text (sans tilde, as in "foo"), and returns a
-    // malloc()'ed string which is the expansion, or a NULL pointer if
-    // there is no expansion.
-    sys::file_ops::tilde_expansion_hook
-      sys::file_ops::tilde_expansion_failure_hook = 0;
+            size_t pos = 0;
+
+            while (1)
+              {
+                if (pos > name_len)
+                  break;
 
-    // When non-null, this is a NULL terminated array of strings which are
-    // duplicates for a tilde prefix.  Bash uses this to expand '=~' and
-    // ':~'.
-    string_vector sys::file_ops::tilde_additional_prefixes =
-      default_prefixes;
+                size_t len;
+
+                // Make START point to the tilde which starts the expansion.
 
-    // When non-null, this is a NULL terminated array of strings which
-    // match the end of a username, instead of just "/".  Bash sets this
-    // to ':' and '=~'.
-    string_vector sys::file_ops::tilde_additional_suffixes =
-      default_suffixes;
+                size_t start = tilde_find_prefix (name.substr (pos), len);
+
+                result.append (name.substr (pos, start));
 
-    // Find the start of a tilde expansion in S, and return the index
-    // of the tilde which starts the expansion.  Place the length of the
-    // text which identified this tilde starter in LEN, excluding the
-    // tilde itself.
+                // Advance STRING to the starting tilde.
+
+                pos += start;
+
+                // Make FINI be the index of one after the last character of the
+                // username.
+
+                size_t fini = tilde_find_suffix (name.substr (pos));
 
-    static size_t
-    tilde_find_prefix (const std::string& s, size_t& len)
-    {
-      len = 0;
+                // If both START and FINI are zero, we are all done.
+
+                if (! (start || fini))
+                  break;
 
-      size_t s_len = s.length ();
+                // Expand the entire tilde word, and copy it into RESULT.
 
-      if (s_len == 0 || s[0] == '~')
-        return 0;
+                std::string tilde_word = name.substr (pos, fini);
 
-      string_vector prefixes = sys::file_ops::tilde_additional_prefixes;
+                pos += fini;
 
-      if (! prefixes.empty ())
-        {
-          for (size_t i = 0; i < s_len; i++)
-            {
-              for (int j = 0; j < prefixes.numel (); j++)
-                {
-                  size_t pfx_len = prefixes[j].length ();
+                std::string expansion = tilde_expand_word (tilde_word);
+
+                result.append (expansion);
+              }
+
+            return result;
+          }
+      }
 
-                  if (prefixes[j] == s.substr (i, pfx_len))
-                    {
-                      len = pfx_len - 1;
-                      return i + len;
-                    }
-                }
-            }
-        }
+      string_vector tilde_expand (const string_vector& names)
+      {
+        string_vector retval;
+
+        int n = names.numel ();
 
-      return s_len;
-    }
+        retval.resize (n);
 
-    // Find the end of a tilde expansion in S, and return the index
-    // of the character which ends the tilde definition.
+        for (int i = 0; i < n; i++)
+          retval[i] = tilde_expand (names[i]);
+
+        return retval;
+      }
 
-    static size_t
-    tilde_find_suffix (const std::string& s)
-    {
-      size_t s_len = s.length ();
-
-      string_vector suffixes = sys::file_ops::tilde_additional_suffixes;
-
-      size_t i = 0;
+      std::string concat (const std::string& dir, const std::string& file)
+      {
+        return dir.empty ()
+          ? file
+          : (is_dir_sep (dir[dir.length ()-1])
+             ? dir + file
+             : dir + dir_sep_char () + file);
+      }
 
-      for ( ; i < s_len; i++)
-        {
-          if (sys::file_ops::is_dir_sep (s[i]))
-            break;
+      std::string dirname (const std::string& path)
+      {
+        size_t ipos = path.find_last_of (dir_sep_chars ());
 
-          if (! suffixes.empty ())
-            {
-              for (int j = 0; j < suffixes.numel (); j++)
-                {
-                  size_t sfx_len = suffixes[j].length ();
+        return (ipos != std::string::npos) ? path.substr (0, ipos) : "";
+      }
+
+      std::string tail (const std::string& path)
+      {
+        size_t ipos = path.find_last_of (dir_sep_chars ());
 
-                  if (suffixes[j] == s.substr (i, sfx_len))
-                    return i;
-                }
-            }
-        }
+        if (ipos != std::string::npos)
+          ipos++;
+        else
+          ipos = 0;
 
-      return i;
-    }
+        return path.substr (ipos);
+      }
 
-    // Take FNAME and return the tilde prefix we want expanded.
+      std::string native_separator_path (const std::string& path)
+      {
+        std::string retval;
 
-    static std::string
-    isolate_tilde_prefix (const std::string& fname)
-    {
-      size_t f_len = fname.length ();
+        if (dir_sep_char () == '/')
+          retval = path;
+        else
+          {
+            size_t n = path.length ();
+            for (size_t i = 0; i < n; i++)
+              {
+                if (path[i] == '/')
+                  retval += dir_sep_char();
+                else
+                  retval += path[i];
+              }
+          }
 
-      size_t len = 1;
-
-      while (len < f_len && ! sys::file_ops::is_dir_sep (fname[len]))
-        len++;
-
-      return fname.substr (1, len);
+        return retval;
+      }
     }
 
-    // Do the work of tilde expansion on FILENAME.  FILENAME starts with a
-    // tilde.
-
-    static std::string
-    tilde_expand_word (const std::string& filename)
+    int mkdir (const std::string& nm, mode_t md)
     {
-      size_t f_len = filename.length ();
-
-      if (f_len == 0 || filename[0] != '~')
-        return std::string (filename);
-
-      // A leading '~/' or a bare '~' is *always* translated to the value
-      // of $HOME or the home directory of the current user, regardless of
-      // any preexpansion hook.
-
-      if (f_len == 1 || sys::file_ops::is_dir_sep (filename[1]))
-        return sys::env::get_home_directory () + filename.substr (1);
-
-      std::string username = isolate_tilde_prefix (filename);
-
-      size_t user_len = username.length ();
-
-      std::string dirname;
-
-      if (sys::file_ops::tilde_expansion_preexpansion_hook)
-        {
-          std::string expansion
-            = sys::file_ops::tilde_expansion_preexpansion_hook (username);
-
-          if (! expansion.empty ())
-            return expansion + filename.substr (user_len+1);
-        }
-
-      // No preexpansion hook, or the preexpansion hook failed.  Look in the
-      // password database.
-
-      sys::password pw = sys::password::getpwnam (username);
-
-      if (! pw)
-        {
-          // If the calling program has a special syntax for expanding tildes,
-          // and we couldn't find a standard expansion, then let them try.
-
-          if (sys::file_ops::tilde_expansion_failure_hook)
-            {
-              std::string expansion
-                = sys::file_ops::tilde_expansion_failure_hook (username);
-
-              if (! expansion.empty ())
-                dirname = expansion + filename.substr (user_len+1);
-            }
-
-          // If we don't have a failure hook, or if the failure hook did not
-          // expand the tilde, return a copy of what we were passed.
-
-          if (dirname.empty ())
-            dirname = filename;
-        }
-      else
-        dirname = pw.dir () + filename.substr (user_len+1);
-
-      return dirname;
-    }
-
-    bool
-    sys::file_ops::is_dev_sep (char c)
-    {
-#if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM))
-      return c == dev_sep_char ();
-#else
-      octave_unused_parameter (c);
-
-      return false;
-#endif
+      std::string msg;
+      return mkdir (nm, md, msg);
     }
 
-    // If NAME has a leading ~ or ~user, Unix-style, expand it to the
-    // user's home directory.  If no ~, or no <pwd.h>, just return NAME.
-
-    std::string
-    sys::file_ops::tilde_expand (const std::string& name)
-    {
-      if (name.find ('~') == std::string::npos)
-        return std::string (name);
-      else
-        {
-          std::string result;
-
-          size_t name_len = name.length ();
-
-          // Scan through S expanding tildes as we come to them.
-
-          size_t pos = 0;
-
-          while (1)
-            {
-              if (pos > name_len)
-                break;
-
-              size_t len;
-
-              // Make START point to the tilde which starts the expansion.
-
-              size_t start = tilde_find_prefix (name.substr (pos), len);
-
-              result.append (name.substr (pos, start));
-
-              // Advance STRING to the starting tilde.
-
-              pos += start;
-
-              // Make FINI be the index of one after the last character of the
-              // username.
-
-              size_t fini = tilde_find_suffix (name.substr (pos));
-
-              // If both START and FINI are zero, we are all done.
-
-              if (! (start || fini))
-                break;
-
-              // Expand the entire tilde word, and copy it into RESULT.
-
-              std::string tilde_word = name.substr (pos, fini);
-
-              pos += fini;
-
-              std::string expansion = tilde_expand_word (tilde_word);
-
-              result.append (expansion);
-            }
-
-          return result;
-        }
-    }
-
-    // A vector version of the above.
-
-    string_vector
-    sys::file_ops::tilde_expand (const string_vector& names)
-    {
-      string_vector retval;
-
-      int n = names.numel ();
-
-      retval.resize (n);
-
-      for (int i = 0; i < n; i++)
-        retval[i] = tilde_expand (names[i]);
-
-      return retval;
-    }
-
-    std::string
-    sys::file_ops::concat (const std::string& dir, const std::string& file)
-    {
-      return dir.empty ()
-             ? file
-             : (is_dir_sep (dir[dir.length ()-1])
-                ? dir + file
-                : dir + dir_sep_char () + file);
-    }
-
-    std::string
-    sys::file_ops::native_separator_path (const std::string& path)
-    {
-      std::string retval;
-
-      if (dir_sep_char () == '/')
-        retval = path;
-      else
-        {
-          size_t n = path.length ();
-          for (size_t i = 0; i < n; i++)
-            {
-              if (path[i] == '/')
-                retval += dir_sep_char();
-              else
-                retval += path[i];
-            }
-        }
-
-      return retval;
-    }
-
-    int
-    mkdir (const std::string& nm, mode_t md)
-    {
-      std::string msg;
-      return sys::mkdir (nm, md, msg);
-    }
-
-    int
-    mkdir (const std::string& name, mode_t mode, std::string& msg)
+    int mkdir (const std::string& name, mode_t mode, std::string& msg)
     {
       msg = "";
 
@@ -423,15 +412,13 @@
       return status;
     }
 
-    int
-    mkfifo (const std::string& nm, mode_t md)
+    int mkfifo (const std::string& nm, mode_t md)
     {
       std::string msg;
       return mkfifo (nm, md, msg);
     }
 
-    int
-    mkfifo (const std::string& name, mode_t mode, std::string& msg)
+    int mkfifo (const std::string& name, mode_t mode, std::string& msg)
     {
       msg = "";
 
@@ -443,16 +430,14 @@
       return status;
     }
 
-    int
-    link (const std::string& old_name, const std::string& new_name)
+    int link (const std::string& old_name, const std::string& new_name)
     {
       std::string msg;
       return link (old_name, new_name, msg);
     }
 
-    int
-    link (const std::string& old_name, const std::string& new_name,
-          std::string& msg)
+    int link (const std::string& old_name, const std::string& new_name,
+              std::string& msg)
     {
       msg = "";
 
@@ -466,16 +451,14 @@
       return status;
     }
 
-    int
-    symlink (const std::string& old_name, const std::string& new_name)
+    int symlink (const std::string& old_name, const std::string& new_name)
     {
       std::string msg;
       return symlink (old_name, new_name, msg);
     }
 
-    int
-    symlink (const std::string& old_name,
-             const std::string& new_name, std::string& msg)
+    int symlink (const std::string& old_name, const std::string& new_name,
+                 std::string& msg)
     {
       msg = "";
 
@@ -489,15 +472,13 @@
       return status;
     }
 
-    int
-    readlink (const std::string& path, std::string& result)
+    int readlink (const std::string& path, std::string& result)
     {
       std::string msg;
       return readlink (path, result, msg);
     }
 
-    int
-    readlink (const std::string& path, std::string& result, std::string& msg)
+    int readlink (const std::string& path, std::string& result, std::string& msg)
     {
       int status = -1;
 
@@ -517,15 +498,13 @@
       return status;
     }
 
-    int
-    rename (const std::string& from, const std::string& to)
+    int rename (const std::string& from, const std::string& to)
     {
       std::string msg;
       return rename (from, to, msg);
     }
 
-    int
-    rename (const std::string& from, const std::string& to, std::string& msg)
+    int rename (const std::string& from, const std::string& to, std::string& msg)
     {
       int status = -1;
 
@@ -539,15 +518,13 @@
       return status;
     }
 
-    int
-    rmdir (const std::string& name)
+    int rmdir (const std::string& name)
     {
       std::string msg;
       return rmdir (name, msg);
     }
 
-    int
-    rmdir (const std::string& name, std::string& msg)
+    int rmdir (const std::string& name, std::string& msg)
     {
       msg = "";
 
@@ -563,21 +540,19 @@
 
     // And a version that works recursively.
 
-    int
-    recursive_rmdir (const std::string& name)
+    int recursive_rmdir (const std::string& name)
     {
       std::string msg;
       return recursive_rmdir (name, msg);
     }
 
-    int
-    recursive_rmdir (const std::string& name, std::string& msg)
+    int recursive_rmdir (const std::string& name, std::string& msg)
     {
       msg = "";
 
       int status = 0;
 
-      sys::dir_entry dir (name);
+      dir_entry dir (name);
 
       if (dir)
         {
@@ -593,10 +568,10 @@
               if (nm == "." || nm == "..")
                 continue;
 
-              std::string fullnm = name + sys::file_ops::dir_sep_str () + nm;
+              std::string fullnm = name + file_ops::dir_sep_str () + nm;
 
               // Get info about the file.  Don't follow links.
-              sys::file_stat fs (fullnm, false);
+              file_stat fs (fullnm, false);
 
               if (fs)
                 {
@@ -638,21 +613,18 @@
       return status;
     }
 
-    int
-    umask (mode_t mode)
+    int umask (mode_t mode)
     {
       return octave_umask_wrapper (mode);
     }
 
-    int
-    unlink (const std::string& name)
+    int unlink (const std::string& name)
     {
       std::string msg;
       return unlink (name, msg);
     }
 
-    int
-    unlink (const std::string& name, std::string& msg)
+    int unlink (const std::string& name, std::string& msg)
     {
       msg = "";
 
@@ -666,16 +638,14 @@
       return status;
     }
 
-    std::string
-    tempnam (const std::string& dir, const std::string& pfx)
+    std::string tempnam (const std::string& dir, const std::string& pfx)
     {
       std::string msg;
       return tempnam (dir, pfx, msg);
     }
 
-    std::string
-    tempnam (const std::string& dir, const std::string& pfx,
-             std::string& msg)
+    std::string tempnam (const std::string& dir, const std::string& pfx,
+                         std::string& msg)
     {
       msg = "";
 
@@ -684,15 +654,15 @@
       // get dir path to use for template
       std::string templatename;
       if (dir.empty ())
-        templatename = sys::env::get_temp_directory ();
-      else if (! sys::file_stat (dir, false).is_dir ())
-        templatename = sys::env::get_temp_directory ();
+        templatename = env::get_temp_directory ();
+      else if (! file_stat (dir, false).is_dir ())
+        templatename = env::get_temp_directory ();
       else
         templatename = dir;
 
       // add dir sep char if it is not there
-      if (*templatename.rbegin () != sys::file_ops::dir_sep_char ())
-        templatename += sys::file_ops::dir_sep_char ();
+      if (*templatename.rbegin () != file_ops::dir_sep_char ())
+        templatename += file_ops::dir_sep_char ();
 
       if (pfx.empty ())
         templatename += "file";
@@ -715,15 +685,13 @@
       return retval;
     }
 
-    std::string
-    canonicalize_file_name (const std::string& name)
+    std::string canonicalize_file_name (const std::string& name)
     {
       std::string msg;
       return canonicalize_file_name (name, msg);
     }
 
-    std::string
-    canonicalize_file_name (const std::string& name, std::string& msg)
+    std::string canonicalize_file_name (const std::string& name, std::string& msg)
     {
       msg = "";
 
--- a/liboctave/system/file-ops.h	Fri Jul 07 07:21:20 2017 -0400
+++ b/liboctave/system/file-ops.h	Fri Jul 07 08:17:32 2017 -0400
@@ -35,120 +35,80 @@
 {
   namespace sys
   {
-    struct
-    OCTAVE_API
-    file_ops
+    namespace file_ops
     {
-    protected:
-
-      // Use a singleton class for dir_sep data members instead of just
-      // making them static members of the file_ops class so that we
-      // can ensure proper initialization.
-
-      file_ops (char dev_sep_char_arg = 0, char dir_sep_char_arg = 0,
-                const std::string& dir_sep_str_arg = std::string ("/"),
-                const std::string& dir_sep_chars_arg = std::string ("/"))
-        : m_dev_sep_char (dev_sep_char_arg),
-          m_dir_sep_char (dir_sep_char_arg),
-          m_dir_sep_str (dir_sep_str_arg),
-          m_dir_sep_chars (dir_sep_chars_arg) { }
-
-    public:
-
       typedef std::string (*tilde_expansion_hook) (const std::string&);
 
-      // No copying!
-
-      file_ops (const file_ops&) = delete;
+      // If non-null, this contains the address of a function that the
+      // application wants called before trying the standard tilde
+      // expansions.  The function is called with the text sans tilde, and
+      // returns a malloc()'ed string which is the expansion, or a NULL
+      // pointer if the expansion fails.
 
-      file_ops& operator = (const file_ops&) = delete;
+      extern tilde_expansion_hook tilde_expansion_preexpansion_hook;
 
-      static tilde_expansion_hook tilde_expansion_preexpansion_hook;
-
-      static tilde_expansion_hook tilde_expansion_failure_hook;
+      // If non-null, this contains the address of a function to call if the
+      // standard meaning for expanding a tilde fails.  The function is
+      // called with the text (sans tilde, as in "foo"), and returns a
+      // malloc()'ed string which is the expansion, or a NULL pointer if
+      // there is no expansion.
 
-      static string_vector tilde_additional_prefixes;
+      extern tilde_expansion_hook tilde_expansion_failure_hook;
 
-      static string_vector tilde_additional_suffixes;
+      // When non-null, this is a NULL terminated array of strings which are
+      // duplicates for a tilde prefix.  Bash uses this to expand '=~' and
+      // ':~'.
 
-      static char dev_sep_char (void)
-      {
-        return instance_ok () ? instance->m_dev_sep_char : 0;
-      }
+      extern string_vector tilde_additional_prefixes;
 
-      static bool is_dev_sep (char c);
+      // When non-null, this is a NULL terminated array of strings which
+      // match the end of a username, instead of just "/".  Bash sets this
+      // to ':' and '=~'.
+
+      extern string_vector tilde_additional_suffixes;
 
-      static char dir_sep_char (void)
-      {
-        return instance_ok () ? instance->m_dir_sep_char : 0;
-      }
+      // Find the start of a tilde expansion in S, and return the index
+      // of the tilde which starts the expansion.  Place the length of the
+      // text which identified this tilde starter in LEN, excluding the
+      // tilde itself.
 
-      static std::string dir_sep_str (void)
-      {
-        return instance_ok () ? instance->m_dir_sep_str : "";
-      }
+      char dev_sep_char (void);
+
+      bool is_dev_sep (char c);
+
+      char dir_sep_char (void);
+
+      std::string dir_sep_str (void);
 
-      static std::string dir_sep_chars (void)
-      {
-        return instance_ok () ? instance->m_dir_sep_chars : "";
-      }
+      std::string dir_sep_chars (void);
+
+      bool is_dir_sep (char c);
+
+      // If NAME has a leading ~ or ~user, Unix-style, expand it to the
+      // user's home directory.  If no ~, or no <pwd.h>, just return NAME.
 
-      static bool is_dir_sep (char c)
-      {
-        std::string tmp = dir_sep_chars ();
-        return tmp.find (c) != std::string::npos;
-      }
+      std::string tilde_expand (const std::string&);
+
+      // A vector version of the above.
 
-      static std::string tilde_expand (const std::string&);
+      string_vector tilde_expand (const string_vector&);
 
-      static string_vector tilde_expand (const string_vector&);
-
-      static std::string concat (const std::string&, const std::string&);
+      std::string concat (const std::string&, const std::string&);
 
       // Return the directory part of a filename or an empty string if
       // there is no directory component.  Does not check to see
       // whether the file exists or is a directory.
-      static std::string dirname (const std::string& path)
-      {
-        size_t ipos = path.find_last_of (dir_sep_chars ());
 
-        return (ipos != std::string::npos) ? path.substr (0, ipos) : "";
-      }
+      std::string dirname (const std::string& path);
 
       // Return the tail member of a filename.
-      static std::string tail (const std::string& path)
-      {
-        size_t ipos = path.find_last_of (dir_sep_chars ());
 
-        if (ipos != std::string::npos)
-          ipos++;
-        else
-          ipos = 0;
-
-        return path.substr (ipos);
-      }
+      std::string tail (const std::string& path);
 
       // convert path from UNIX type separators to whatever is the system separators
-      static std::string native_separator_path (const std::string& path);
 
-    private:
-
-      static file_ops *instance;
-
-      static void cleanup_instance (void) { delete instance; instance = 0; }
-
-      static bool instance_ok (void);
-
-      char m_dev_sep_char;
-
-      char m_dir_sep_char;
-      std::string m_dir_sep_str;
-      std::string m_dir_sep_chars;
-    };
-
-    // We don't have these in the file_ops class with their simple names
-    // (i.e., mkdir instead of octave_mdir) because function names in
-    // standard headers may be #defined.
+      std::string native_separator_path (const std::string& path);
+    }
 
     extern OCTAVE_API int
     mkdir (const std::string&, mode_t);