o removed waywars #endif
[asdcplib.git] / src / KM_fileio.cpp
index d5d5a40e8c6b1b526272cb39b63012759e855401..0dfa473dda79cbef35ee4557e7dae176a652342a 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2009, John Hurst
+Copyright (c) 2004-2016, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -37,6 +37,19 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifdef KM_WIN32
 #include <direct.h>
+#else
+#define _getcwd getcwd
+#define _unlink unlink
+#define _rmdir rmdir
+#endif
+
+// only needed by GetExecutablePath()
+#if defined(KM_MACOSX)
+#include <mach-o/dyld.h>
+#endif
+
+#if defined(__OpenBSD__)
+#include <sys/sysctl.h>
 #endif
 
 using namespace Kumu;
@@ -57,6 +70,7 @@ struct iovec {
 # if defined(__linux__)
 #   include <sys/statfs.h>
 # else
+#  include <sys/param.h>
 #  include <sys/mount.h>
 # endif
 
@@ -65,31 +79,9 @@ struct iovec {
 typedef struct stat     fstat_t;
 #endif
 
-//
-static void
-split(const std::string& str, char separator, std::list<std::string>& components)
-{
-  const char* pstr = str.c_str();
-  const char* r = strchr(pstr, separator);
-
-  while ( r != 0 )
-    {
-      assert(r >= pstr);
-      if ( r > pstr )
-       {
-         std::string tmp_str;
-         tmp_str.assign(pstr, (r - pstr));
-         components.push_back(tmp_str);
-       }
-
-      pstr = r + 1;
-      r = strchr(pstr, separator);
-    }
-
-  if( strlen(pstr) > 0 )
-    components.push_back(std::string(pstr));
-}
-
+#if defined(__sun) && defined(__SVR4)
+#include <sys/statfs.h>
+#endif
 
 //
 static Kumu::Result_t
@@ -211,62 +203,66 @@ Kumu::FileSize(const std::string& pathname)
 }
 
 //
-static PathCompList_t&
-s_PathMakeCanonical(PathCompList_t& CList, char separator, bool is_absolute)
+static void
+make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
 {
-  PathCompList_t::iterator ci, ri; // component and removal iterators
-
-  for ( ci = CList.begin(); ci != CList.end(); ci++ )
+  PathCompList_t::const_iterator i;
+  for ( i = in_list.begin(); i != in_list.end(); ++i )
     {
-      if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
-        {
-          ri = ci++;
-          CList.erase(ri);
-        }
-      else if ( *ci == ".." && ci != CList.begin() )
+      if ( *i == ".." )
        {
-         ri = ci;
-         ri--;
-             
-         if ( *ri != ".." )
+         if ( ! out_list.empty() )
            {
-             CList.erase(ri);
-             ri = ci++;
-             CList.erase(ri);
-            }
-        }
+             out_list.pop_back();
+           }
+       }
+      else if ( *i != "." )
+       {
+         out_list.push_back(*i);
+       }
     }
-
-  return CList;
 }
 
 //
 std::string
 Kumu::PathMakeCanonical(const std::string& Path, char separator)
 {
-  PathCompList_t CList;
+  PathCompList_t in_list, out_list;
   bool is_absolute = PathIsAbsolute(Path, separator);
-  s_PathMakeCanonical(PathToComponents(Path, CList, separator), separator, is_absolute);
+  PathToComponents(Path, in_list, separator);
+  make_canonical_list(in_list, out_list);
 
   if ( is_absolute )
-    return ComponentsToAbsolutePath(CList, separator);
+    return ComponentsToAbsolutePath(out_list, separator);
 
-  return ComponentsToPath(CList, separator);
+  return ComponentsToPath(out_list, separator);
 }
 
 //
 bool
 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
 {
-  return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
+  return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
 }
 
 //
 Kumu::PathCompList_t&
-Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
+Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
 {
-  split(Path, separator, CList);
-  return CList;
+  std::string s;
+  s = separator;
+  PathCompList_t tmp_list = km_token_split(path, std::string(s));
+  PathCompList_t::const_iterator i;
+
+  for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
+    {
+      if ( ! i->empty() )
+       {
+         component_list.push_back(*i);
+       }
+    }
+
+  return component_list;
 }
 
 //
@@ -329,30 +325,37 @@ Kumu::PathIsAbsolute(const std::string& Path, char separator)
 
 //
 std::string
-Kumu::PathMakeAbsolute(const std::string& Path, char separator)
+Kumu::PathCwd()
 {
-  if ( Path.empty() )
+  char cwd_buf [MaxFilePath];
+  if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
     {
-      std::string out_path;
-      out_path = separator;
-      return out_path;
+      DefaultLogSink().Error("Error retrieving current working directory.");
+      return "";
     }
 
-  if ( PathIsAbsolute(Path, separator) )
-    return Path;
+  return cwd_buf;
+}
 
-  char cwd_buf [MaxFilePath];
-  if ( getcwd(cwd_buf, MaxFilePath) == 0 )
+//
+std::string
+Kumu::PathMakeAbsolute(const std::string& Path, char separator)
+{
+  if ( Path.empty() )
     {
-      DefaultLogSink().Error("Error retrieving current working directory.");
-      return "";
+      std::string tmpstr;
+      tmpstr = separator;
+      return tmpstr;
     }
 
-  PathCompList_t CList;
-  CList.push_back(cwd_buf);
-  CList.push_back(Path);
+  if ( PathIsAbsolute(Path, separator) )
+    return PathMakeCanonical(Path);
+
+  PathCompList_t in_list, out_list;
+  PathToComponents(PathJoin(PathCwd(), Path), in_list);
+  make_canonical_list(in_list, out_list);
 
-  return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, separator, true), separator);
+  return ComponentsToAbsolutePath(out_list);
 }
 
 //
@@ -428,6 +431,91 @@ Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) //
   return Basename + "." + Extension;
 }
 
+//
+std::string
+Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
+{
+  return Path1 + separator + Path2;
+}
+
+//
+std::string
+Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
+{
+  return Path1 + separator + Path2 + separator + Path3;
+}
+
+//
+std::string
+Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
+              const std::string& Path3, const std::string& Path4, char separator)
+{
+  return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
+}
+
+#ifndef KM_WIN32
+// returns false if link cannot be read
+//
+bool
+Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
+{
+  PathCompList_t in_list, out_list;
+  PathToComponents(PathMakeCanonical(link_path), in_list, separator);
+  PathCompList_t::iterator i;
+  char link_buf[MaxFilePath];
+
+  for ( i = in_list.begin(); i != in_list.end(); ++i )
+    {
+      assert ( *i != ".." && *i != "." );
+      out_list.push_back(*i);
+
+      for (;;)
+       {
+         std::string next_link = ComponentsToAbsolutePath(out_list, separator);
+         ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
+
+         if ( link_size == -1 )
+           {
+             if ( errno == EINVAL )
+               break;
+
+             DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
+             return false;
+           }
+         
+         assert(link_size < MaxFilePath);
+         link_buf[link_size] = 0;
+         std::string tmp_path;
+         out_list.clear();
+
+         if ( PathIsAbsolute(link_buf) )
+           {
+             tmp_path = link_buf;
+           }
+         else
+           {
+             tmp_path = PathJoin(PathDirname(next_link), link_buf);
+           }
+
+         PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
+       }
+    }
+
+  resolved_path = ComponentsToAbsolutePath(out_list, separator);
+  return true;
+}
+
+#else // KM_WIN32
+// TODO: is there a reasonable equivalent to be written for win32?
+//
+bool
+Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
+{
+  resolved_path = link_path;
+  return true;
+}
+#endif
+
 //
 Kumu::PathList_t&
 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
@@ -550,6 +638,66 @@ Kumu::PathMatchGlob::Match(const std::string& s) const {
 
 #endif
 
+
+//------------------------------------------------------------------------------------------
+
+#define X_BUFSIZE 1024
+
+//
+std::string
+Kumu::GetExecutablePath(const std::string& default_path)
+{
+  char path[X_BUFSIZE] = {0};
+  bool success = false;
+
+#if defined(KM_WIN32)
+  DWORD size = X_BUFSIZE;
+  DWORD rc = GetModuleFileName(0, path, size);
+  success = ( rc != 0 );
+#elif defined(KM_MACOSX)
+  uint32_t size = X_BUFSIZE;
+  int rc = _NSGetExecutablePath(path, &size);
+  success = ( rc != -1 );
+#elif defined(__linux__)
+  size_t size = X_BUFSIZE;
+  ssize_t rc = readlink("/proc/self/exe", path, size);
+  success = ( rc != -1 );
+#elif defined(__OpenBSD__)
+  // This fails if the CWD changes after the program has started but before the
+  // call to GetExecutablePath(). For least surprise, call GetExecutablePath()
+  // immediately in main() and save the value for later use.
+  const  char* p = getenv("_");
+  if ( p )
+    {
+      return Kumu::PathMakeAbsolute(p);
+    }
+#elif defined(__FreeBSD__)
+  // requires procfs
+  size_t size = X_BUFSIZE;
+  ssize_t rc = readlink("/proc/curproc/file", path, size);
+  success = ( rc != -1 );
+#elif defined(__NetBSD__)
+  size_t size = X_BUFSIZE;
+  ssize_t rc = readlink("/proc/curproc/exe", path, size);
+  success = ( rc != -1 );
+#elif defined(__sun) && defined(__SVR4)
+  size_t size = X_BUFSIZE;
+  char program[MAXPATHLEN];
+  snprintf(program, MAXPATHLEN, "/proc/%d/path/a.out", getpid());
+  ssize_t rc = readlink(program, path, size);
+#else
+#error GetExecutablePath --> Create a method for obtaining the executable name
+#endif
+
+  if ( success )
+    {
+      return Kumu::PathMakeCanonical(path);
+    }
+
+  return default_path;
+}
+
+
 //------------------------------------------------------------------------------------------
 // portable aspects of the file classes
 
@@ -614,19 +762,73 @@ Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
 
 
 #ifdef KM_WIN32
+
+//
+Kumu::Result_t
+Kumu::wbstr_to_utf8(const Kumu::ByteString& in, std::string& out)
+{
+  out.erase();
+  assert(in.Length()%sizeof(wchar_t)==0);
+  const wchar_t* p = (const wchar_t*)in.RoData();
+
+  int stringLength = static_cast<int>( in.Length() );
+  int len = WideCharToMultiByte(CP_UTF8, 0, p, stringLength, NULL, 0, NULL, NULL);
+  char *mb_buf = new char[len];
+  WideCharToMultiByte(CP_UTF8, 0, p, stringLength, mb_buf, len, NULL, NULL);
+  out = mb_buf;
+  delete [] mb_buf;
+  return RESULT_OK;
+}
+
+//
+Kumu::Result_t
+Kumu::utf8_to_wbstr(const std::string& in, Kumu::ByteString& out)
+{
+  Result_t result = out.Capacity((in.size()+1)*sizeof(wchar_t));
+
+  if ( KM_FAILURE(result) )
+    {
+      return result;
+    }
+
+  assert(in.size()*sizeof(wchar_t)<=out.Capacity());
+  const char* read_pos = in.c_str();
+  wchar_t character, *write_pos = (wchar_t*)out.Data();
+
+  int stringLength = static_cast<int>( in.length() ) + 1;
+  int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, 0, 0);
+  result = out.Capacity(len*sizeof(wchar_t));
+  if ( KM_FAILURE(result) )
+    {
+      return result;
+    }
+  MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, write_pos, len);
+  out.Length(len*sizeof(wchar_t));
+
+  return RESULT_OK;
+}
+
+
 //------------------------------------------------------------------------------------------
 //
 
 Kumu::Result_t
-Kumu::FileReader::OpenRead(const char* filename) const
+Kumu::FileReader::OpenRead(const std::string& filename) const
 {
-  KM_TEST_NULL_STR_L(filename);
   const_cast<FileReader*>(this)->m_Filename = filename;
-  
+  ByteString wb_filename;
+  Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
+
+  if ( KM_FAILURE(result) )
+    {
+      return result;
+    }
+
   // suppress popup window on error
   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
 
-  const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
+  const_cast<FileReader*>(this)->m_Handle =
+            ::CreateFileW((wchar_t*)wb_filename.RoData(),
                          (GENERIC_READ),                // open for reading
                          FILE_SHARE_READ,               // share for reading
                          NULL,                          // no security
@@ -741,17 +943,24 @@ Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
 //------------------------------------------------------------------------------------------
 //
 
+
 //
 Kumu::Result_t
-Kumu::FileWriter::OpenWrite(const char* filename)
+Kumu::FileWriter::OpenWrite(const std::string& filename)
 {
-  KM_TEST_NULL_STR_L(filename);
   m_Filename = filename;
-  
+  ByteString wb_filename;
+  Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
+
+  if ( KM_FAILURE(result) )
+    {
+      return result;
+    }
+
   // suppress popup window on error
   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
 
-  m_Handle = ::CreateFile(filename,
+  m_Handle = ::CreateFileW((wchar_t*)wb_filename.RoData(),
                          (GENERIC_WRITE|GENERIC_READ),  // open for reading
                          FILE_SHARE_READ,               // share for reading
                          NULL,                          // no security
@@ -842,11 +1051,10 @@ Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written
 
 //
 Kumu::Result_t
-Kumu::FileReader::OpenRead(const char* filename) const
+Kumu::FileReader::OpenRead(const std::string& filename) const
 {
-  KM_TEST_NULL_STR_L(filename);
   const_cast<FileReader*>(this)->m_Filename = filename;
-  const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
+  const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
   return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
 }
 
@@ -922,15 +1130,14 @@ Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
 
 //
 Kumu::Result_t
-Kumu::FileWriter::OpenWrite(const char* filename)
+Kumu::FileWriter::OpenWrite(const std::string& filename)
 {
-  KM_TEST_NULL_STR_L(filename);
   m_Filename = filename;
-  m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
+  m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0666);
 
   if ( m_Handle == -1L )
     {
-      DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
+      DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
       return RESULT_FILEOPEN;
     }
 
@@ -940,15 +1147,14 @@ Kumu::FileWriter::OpenWrite(const char* filename)
 
 //
 Kumu::Result_t
-Kumu::FileWriter::OpenModify(const char* filename)
+Kumu::FileWriter::OpenModify(const std::string& filename)
 {
-  KM_TEST_NULL_STR_L(filename);
   m_Filename = filename;
-  m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
+  m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0666);
 
   if ( m_Handle == -1L )
     {
-      DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
+      DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
       return RESULT_FILEOPEN;
     }
 
@@ -1014,15 +1220,13 @@ Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written
 
 //
 Kumu::Result_t
-Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
+Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
 {
   fsize_t    fsize = 0;
   ui32_t     read_size = 0;
   FileReader File;
   ByteString ReadBuf;
 
-  KM_TEST_NULL_STR_L(filename);
-
   Result_t result = File.OpenRead(filename);
 
   if ( KM_SUCCESS(result) )
@@ -1031,13 +1235,13 @@ Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t ma
 
       if ( fsize > (Kumu::fpos_t)max_size )
        {
-         DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
+         DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
          return RESULT_ALLOC;
        }
 
       if ( fsize == 0 )
        {
-         DefaultLogSink().Error("%s: zero file size\n", filename);
+         DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
          return RESULT_READFAIL;
        }
 
@@ -1056,11 +1260,10 @@ Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t ma
 
 //
 Kumu::Result_t
-Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
+Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
 {
   FileWriter File;
   ui32_t write_count = 0;
-  KM_TEST_NULL_STR_L(filename);
 
   Result_t result = File.OpenWrite(filename);
 
@@ -1084,9 +1287,9 @@ Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui
   if ( KM_SUCCESS(result) )
     {
       ui32_t read_count = 0;
-      FileWriter Reader;
+      FileReader Reader;
 
-      result = Reader.OpenRead(Filename.c_str());
+      result = Reader.OpenRead(Filename);
 
       if ( KM_SUCCESS(result) )
        result = Reader.Read(Buffer.Data(), file_size, &read_count);
@@ -1121,7 +1324,7 @@ Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filen
       if ( KM_SUCCESS(result) )
        {
          Buffer.Length(MemWriter.Length());
-         result = Writer.OpenWrite(Filename.c_str());
+         result = Writer.OpenWrite(Filename);
        }
 
       if ( KM_SUCCESS(result) )
@@ -1144,9 +1347,9 @@ Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer,
   if ( KM_SUCCESS(result) )
     {
       ui32_t read_count = 0;
-      FileWriter Reader;
+      FileReader Reader;
 
-      result = Reader.OpenRead(Filename.c_str());
+      result = Reader.OpenRead(Filename);
 
       if ( KM_SUCCESS(result) )
        result = Reader.Read(Buffer.Data(), file_size, &read_count);
@@ -1170,7 +1373,7 @@ Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Fil
   ui32_t write_count = 0;
   FileWriter Writer;
 
-  Result_t result = Writer.OpenWrite(Filename.c_str());
+  Result_t result = Writer.OpenWrite(Filename);
 
   if ( KM_SUCCESS(result) )
     result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
@@ -1185,162 +1388,224 @@ Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Fil
 //
 
 
-// Win32 directory scanner
 //
-#ifdef KM_WIN32
+Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
 
-//
 //
 Result_t
-Kumu::DirScanner::Open(const char* filename)
+Kumu::DirScanner::Open(const std::string& dirname)
 {
-  KM_TEST_NULL_STR_L(filename);
-
-  // we need to append a '*' to read the entire directory
-  ui32_t fn_len = strlen(filename); 
-  char* tmp_file = (char*)malloc(fn_len + 8);
-
-  if ( tmp_file == 0 )
-    return RESULT_ALLOC;
-
-  strcpy(tmp_file, filename);
-  char* p = &tmp_file[fn_len] - 1;
+  Result_t result = RESULT_OK;
 
-  if ( *p != '/' && *p != '\\' )
+  if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
     {
-      p++;
-      *p++ = '/';
+      switch ( errno )
+       {
+       case ENOENT:
+       case ENOTDIR:
+         result = RESULT_NOTAFILE;
+       case EACCES:
+         result = RESULT_NO_PERM;
+       case ELOOP:
+       case ENAMETOOLONG:
+         result = RESULT_PARAM;
+       case EMFILE:
+       case ENFILE:
+         result = RESULT_STATE;
+       default:
+         DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
+         result = RESULT_FAIL;
+       }
     }
 
-  *p++ = '*';
-  *p = 0;
-  // whew...
-
-  m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
-  Result_t result = RESULT_OK;
-
-  if ( m_Handle == -1 )
-    result = RESULT_NOT_FOUND;
-
   return result;
 }
 
 
-//
 //
 Result_t
 Kumu::DirScanner::Close()
 {
-  if ( m_Handle == -1 )
+  if ( m_Handle == NULL )
     return RESULT_FILEOPEN;
 
-  if ( _findclose((long)m_Handle) == -1 )
-    return RESULT_FAIL;
+  if ( closedir(m_Handle) == -1 ) {
+    switch ( errno )
+      {
+      case EBADF:
+      case EINTR:
+       return RESULT_STATE;
+      default:
+       DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
+       return RESULT_FAIL;
+      }
+  }
 
-  m_Handle = -1;
+  m_Handle = NULL;
   return RESULT_OK;
 }
 
 
-// This sets filename param to the same per-instance buffer every time, so
-// the value will change on the next call
+//
 Result_t
 Kumu::DirScanner::GetNext(char* filename)
 {
   KM_TEST_NULL_L(filename);
 
-  if ( m_Handle == -1 )
+  if ( m_Handle == NULL )
     return RESULT_FILEOPEN;
 
-  if ( m_FileInfo.name[0] == '\0' )
-    return RESULT_ENDOFFILE;
-
-  strncpy(filename, m_FileInfo.name, MaxFilePath);
-  Result_t result = RESULT_OK;
+  struct dirent* entry;
 
-  if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
+  for (;;)
     {
-      m_FileInfo.name[0] = '\0';
-         
-      if ( errno != ENOENT )
-       result = RESULT_FAIL;
+      if ( ( entry = readdir(m_Handle)) == NULL )
+       return RESULT_ENDOFFILE;
+
+      break;
     }
 
-  return result;
+  strncpy(filename, entry->d_name, MaxFilePath);
+  return RESULT_OK;
 }
 
+//------------------------------------------------------------------------------------------
 
-#else // KM_WIN32
-
-// POSIX directory scanner
+//
+Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
 
 //
 Result_t
-Kumu::DirScanner::Open(const char* filename)
+Kumu::DirScannerEx::Open(const std::string& dirname)
 {
-  KM_TEST_NULL_STR_L(filename);
-
   Result_t result = RESULT_OK;
 
-  if ( ( m_Handle = opendir(filename) ) == NULL )
+  if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
     {
-      if ( errno == ENOENT )
-       result = RESULT_ENDOFFILE;
-
-      else
-       result = RESULT_FAIL;
+      switch ( errno )
+       {
+       case ENOENT:
+       case ENOTDIR:
+         result = RESULT_NOTAFILE;
+       case EACCES:
+         result = RESULT_NO_PERM;
+       case ELOOP:
+       case ENAMETOOLONG:
+         result = RESULT_PARAM;
+       case EMFILE:
+       case ENFILE:
+         result = RESULT_STATE;
+       default:
+         DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
+         result = RESULT_FAIL;
+       }
     }
 
+  if ( KM_SUCCESS(result) )
+    m_Dirname = dirname;
+
+  KM_RESULT_STATE_TEST_IMPLICIT();
   return result;
 }
 
-
 //
 Result_t
-Kumu::DirScanner::Close()
+Kumu::DirScannerEx::Close()
 {
   if ( m_Handle == NULL )
     return RESULT_FILEOPEN;
 
   if ( closedir(m_Handle) == -1 )
-    return RESULT_FAIL;
+    {
+      switch ( errno )
+       {
+       case EBADF:
+       case EINTR:
+         KM_RESULT_STATE_HERE();
+         return RESULT_STATE;
 
-  m_Handle = NULL;
+       default:
+         DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
+         return RESULT_FAIL;
+       }
+    }
+
+  m_Handle = 0;
   return RESULT_OK;
 }
 
-
 //
 Result_t
-Kumu::DirScanner::GetNext(char* filename)
+Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
 {
-  KM_TEST_NULL_L(filename);
-
-  if ( m_Handle == NULL )
+  if ( m_Handle == 0 )
     return RESULT_FILEOPEN;
 
+#if defined(__sun) && defined(__SVR4)
+  struct stat s;
+#endif
   struct dirent* entry;
 
   for (;;)
     {
-      if ( ( entry = readdir(m_Handle)) == NULL )
+      if ( ( entry = readdir(m_Handle) ) == 0 )
        return RESULT_ENDOFFILE;
 
       break;
     }
 
-  strncpy(filename, entry->d_name, MaxFilePath);
-  return RESULT_OK;
-}
+  next_item_name.assign(entry->d_name, strlen(entry->d_name));
 
+#if defined(__sun) && defined(__SVR4)
 
-#endif // KM_WIN32
+  stat(entry->d_name, &s);
+
+  switch ( s.st_mode )
+    {
+    case S_IFDIR:
+      next_item_type = DET_DIR;
+      break;
+
+    case S_IFREG:
+      next_item_type = DET_FILE;
+      break;
+
+    case S_IFLNK:
+      next_item_type = DET_LINK;
+      break;
+
+    default:
+      next_item_type = DET_DEV;
+    }
+#else // __sun 
+  switch ( entry->d_type )
+    {
+    case DT_DIR:
+      next_item_type = DET_DIR;
+      break;
+
+    case DT_REG:
+      next_item_type = DET_FILE;
+      break;
+
+    case DT_LNK:
+      next_item_type = DET_LINK;
+      break;
+
+    default:
+      next_item_type = DET_DEV;
+    }
+#endif // __sun
+  return RESULT_OK;
+}
 
 
 //------------------------------------------------------------------------------------------
 
-// note: when moving to KM_fileio, don't forget to write the Win32 versions
-// note: add error messages and remove RESULT_FAIL form DirScanner
+//
+// Attention Windows users: make sure to use the proper separator character
+// with these functions.
+//
 
 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
 //
@@ -1348,7 +1613,6 @@ Result_t
 Kumu::CreateDirectoriesInPath(const std::string& Path)
 {
   bool abs = PathIsAbsolute(Path);
-  assert(abs);
   PathCompList_t PathComps, TmpPathComps;
 
   PathToComponents(Path, PathComps);
@@ -1362,9 +1626,9 @@ Kumu::CreateDirectoriesInPath(const std::string& Path)
       if ( ! PathIsDirectory(tmp_path) )
        {
 #ifdef KM_WIN32
-         if ( mkdir(tmp_path.c_str()) != 0 )
+         if ( _mkdir(tmp_path.c_str()) != 0 )
 #else // KM_WIN32
-         if ( mkdir(tmp_path.c_str(), 0775) != 0 )
+         if ( mkdir(tmp_path.c_str(), 0777) != 0 )
 #endif // KM_WIN32
            {
              DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
@@ -1382,7 +1646,7 @@ Kumu::CreateDirectoriesInPath(const std::string& Path)
 Result_t
 Kumu::DeleteFile(const std::string& filename)
 {
-  if ( unlink(filename.c_str()) == 0 )
+  if ( _unlink(filename.c_str()) == 0 )
     return RESULT_OK;
 
   switch ( errno )
@@ -1404,7 +1668,9 @@ Kumu::DeleteFile(const std::string& filename)
 Result_t
 h__DeletePath(const std::string& pathname)
 {
-  fprintf(stderr, "h__DeletePath %s\n", pathname.c_str());
+  if ( pathname.empty() )
+    return RESULT_NULL_STR;
+
   Result_t result = RESULT_OK;
 
   if ( ! PathIsDirectory(pathname) )
@@ -1433,7 +1699,7 @@ h__DeletePath(const std::string& pathname)
          }
       }
 
-      if ( rmdir(pathname.c_str()) != 0 )
+      if ( _rmdir(pathname.c_str()) != 0 )
        {
          switch ( errno )
            {
@@ -1463,25 +1729,77 @@ h__DeletePath(const std::string& pathname)
 Result_t
 Kumu::DeletePath(const std::string& pathname)
 {
-  std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
+  std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
   DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
   return h__DeletePath(c_pathname);
 }
 
 
-//------------------------------------------------------------------------------------------
 //
+Result_t
+Kumu::DeleteDirectoryIfEmpty(const std::string& path)
+{
+  DirScanner source_dir;
+  char next_file[Kumu::MaxFilePath];
 
+  Result_t result = source_dir.Open(path);
 
-#ifdef KM_WIN32
-#else // KM_WIN32
+  if ( KM_FAILURE(result) )
+    return result;
+
+  while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
+    {
+      if ( ( next_file[0] == '.' && next_file[1] == 0 )
+          || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
+       continue;
+
+      return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
+    }
+
+  return DeletePath(path);
+}
 
+
+//------------------------------------------------------------------------------------------
 //
+
+
 Result_t
 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
 {
+#ifdef KM_WIN32
+  ULARGE_INTEGER lTotalNumberOfBytes;
+  ULARGE_INTEGER lTotalNumberOfFreeBytes;
+
+  BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
+  if ( fResult )
+    {
+      free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
+      total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
+      return RESULT_OK;
+    }
+
+  HRESULT last_error = ::GetLastError();
+
+  DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
+  return RESULT_FAIL;
+#else // KM_WIN32
   struct statfs s;
 
+#if defined(__sun) && defined(__SVR4)
+  if ( statfs(path.c_str(), &s, s.f_bsize, s.f_fstyp ) == 0 )
+    {      if ( s.f_blocks < 1 )
+       {
+         DefaultLogSink().Error("File system %s has impossible size: %ld\n",
+                                path.c_str(), s.f_blocks);
+         return RESULT_FAIL;
+       }
+
+      free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bfree;
+      total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
+      return RESULT_OK;
+    }
+#else
   if ( statfs(path.c_str(), &s) == 0 )
     {
       if ( s.f_blocks < 1 )
@@ -1505,9 +1823,10 @@ Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu:
 
   DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
   return RESULT_FAIL;
+#endif // __sun 
+#endif // KM_WIN32
 } 
 
-#endif // KM_WIN32
 
 //
 // end KM_fileio.cpp