removed nascent ST2052-1 support pending completion
[asdcplib.git] / src / KM_fileio.cpp
index 1ed32fb7d462a96669ca4edbad6075142dc2af6e..0866acd9e0ab8adf3f6702fe7550b0397dcf1204 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2006, John Hurst
+Copyright (c) 2004-2012, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -32,29 +32,51 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <KM_fileio.h>
 #include <KM_log.h>
 #include <fcntl.h>
+
 #include <assert.h>
 
+#ifdef KM_WIN32
+#include <direct.h>
+#else
+#define _getcwd getcwd
+#define _unlink unlink
+#define _rmdir rmdir
+#endif
+
 using namespace Kumu;
 
 #ifdef KM_WIN32
 typedef struct _stati64 fstat_t;
+#define S_IFLNK 0
+
 
-// AFAIK, there is no iovec equivalent in the win32 API
+// win32 has WriteFileGather() and ReadFileScatter() but they
+// demand page alignment and page sizing, making them unsuitable
+// for use with arbitrary buffer sizes.
 struct iovec {
   char* iov_base; // stupid iovec uses char*
   int   iov_len;
 };
 #else
+# if defined(__linux__)
+#   include <sys/statfs.h>
+# else
+#  include <sys/param.h>
+#  include <sys/mount.h>
+# endif
+
+#include <sys/stat.h>
 #include <sys/uio.h>
 typedef struct stat     fstat_t;
 #endif
 
+
 //
 static Kumu::Result_t
 do_stat(const char* path, fstat_t* stat_info)
 {
-  KM_TEST_NULL_STR(path);
-  KM_TEST_NULL(stat_info);
+  KM_TEST_NULL_STR_L(path);
+  KM_TEST_NULL_L(stat_info);
 
   Kumu::Result_t result = Kumu::RESULT_OK;
 
@@ -69,7 +91,7 @@ do_stat(const char* path, fstat_t* stat_info)
   if ( stat(path, stat_info) == -1L )
     result = Kumu::RESULT_FILEOPEN;
 
-  if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
+  if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
     result = Kumu::RESULT_FILEOPEN;
 #endif
 
@@ -80,16 +102,16 @@ do_stat(const char* path, fstat_t* stat_info)
 
 //
 static Kumu::Result_t
-do_fstat(HANDLE handle, fstat_t* stat_info)
+do_fstat(FileHandle handle, fstat_t* stat_info)
 {
-  KM_TEST_NULL(stat_info);
+  KM_TEST_NULL_L(stat_info);
 
   Kumu::Result_t result = Kumu::RESULT_OK;
 
   if ( fstat(handle, stat_info) == -1L )
     result = Kumu::RESULT_FILEOPEN;
 
-  if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
+  if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
     result = Kumu::RESULT_FILEOPEN;
 
   return result;
@@ -100,14 +122,31 @@ do_fstat(HANDLE handle, fstat_t* stat_info)
 
 //
 bool
-Kumu::PathIsFile(const char* pathname)
+Kumu::PathExists(const std::string& pathname)
 {
-  assert(pathname);
+  if ( pathname.empty() )
+    return false;
+
   fstat_t info;
 
-  if ( KM_SUCCESS(do_stat(pathname, &info)) )
+  if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
+    return true;
+
+  return false;
+}
+
+//
+bool
+Kumu::PathIsFile(const std::string& pathname)
+{
+  if ( pathname.empty() )
+    return false;
+
+  fstat_t info;
+
+  if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
     {
-      if ( info.st_mode & S_IFREG )
+      if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
         return true;
     }
 
@@ -117,12 +156,14 @@ Kumu::PathIsFile(const char* pathname)
 
 //
 bool
-Kumu::PathIsDirectory(const char* pathname)
+Kumu::PathIsDirectory(const std::string& pathname)
 {
-  assert(pathname);
+  if ( pathname.empty() )
+    return false;
+
   fstat_t info;
 
-  if ( KM_SUCCESS(do_stat(pathname, &info)) )
+  if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
     {
       if ( info.st_mode & S_IFDIR )
         return true;
@@ -131,23 +172,450 @@ Kumu::PathIsDirectory(const char* pathname)
   return false;
 }
 
-
 //
 Kumu::fsize_t
-Kumu::FileSize(const char* pathname)
+Kumu::FileSize(const std::string& pathname)
 {
-  assert(pathname);
+  if ( pathname.empty() )
+    return 0;
+
   fstat_t info;
 
-  if ( KM_SUCCESS(do_stat(pathname, &info)) )
+  if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
     {
-      if ( info.st_mode & S_IFREG )
+      if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
         return(info.st_size);
     }
 
   return 0;
 }
 
+//
+static void
+make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
+{
+  PathCompList_t::const_iterator i;
+  for ( i = in_list.begin(); i != in_list.end(); ++i )
+    {
+      if ( *i == ".." )
+       {
+         if ( ! out_list.empty() )
+           {
+             out_list.pop_back();
+           }
+       }
+      else if ( *i != "." )
+       {
+         out_list.push_back(*i);
+       }
+    }
+}
+
+//
+std::string
+Kumu::PathMakeCanonical(const std::string& Path, char separator)
+{
+  PathCompList_t in_list, out_list;
+  bool is_absolute = PathIsAbsolute(Path, separator);
+  PathToComponents(Path, in_list, separator);
+  make_canonical_list(in_list, out_list);
+
+  if ( is_absolute )
+    return ComponentsToAbsolutePath(out_list, separator);
+
+  return ComponentsToPath(out_list, separator);
+}
+
+//
+bool
+Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
+{
+  return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
+}
+
+//
+Kumu::PathCompList_t&
+Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
+{
+  std::string s;
+  s = separator;
+  CList = km_token_split(Path, s);
+  return CList;
+}
+
+//
+std::string
+Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
+{
+  if ( CList.empty() )
+    return "";
+
+  PathCompList_t::const_iterator ci = CList.begin();
+  std::string out_path = *ci;
+
+  for ( ci++; ci != CList.end(); ci++ )
+    out_path += separator + *ci;
+
+  return out_path;
+}
+
+//
+std::string
+Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
+{
+  std::string out_path;
+
+  if ( CList.empty() )
+    out_path = separator;
+  else
+    {
+      PathCompList_t::const_iterator ci;
+
+      for ( ci = CList.begin(); ci != CList.end(); ci++ )
+       out_path += separator + *ci;
+    }
+
+  return out_path;
+}
+
+//
+bool
+Kumu::PathHasComponents(const std::string& Path, char separator)
+{
+  if ( strchr(Path.c_str(), separator) == 0 )
+    return false;
+
+  return true;
+}
+
+//
+bool
+Kumu::PathIsAbsolute(const std::string& Path, char separator)
+{
+  if ( Path.empty() )
+    return false;
+
+  if ( Path[0] == separator)
+    return true;
+
+  return false;
+}
+
+//
+std::string
+Kumu::PathCwd()
+{
+  char cwd_buf [MaxFilePath];
+  if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
+    {
+      DefaultLogSink().Error("Error retrieving current working directory.");
+      return "";
+    }
+
+  return cwd_buf;
+}
+
+//
+std::string
+Kumu::PathMakeAbsolute(const std::string& Path, char separator)
+{
+  if ( Path.empty() )
+    {
+      std::string tmpstr;
+      tmpstr = separator;
+      return tmpstr;
+    }
+
+  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(out_list);
+}
+
+//
+std::string
+Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
+{
+  size_t pos = Path.find(Parent);
+
+  if ( pos == 0 ) // Parent found at offset 0
+    return Path.substr(Parent.size()+1);
+
+  return Path;
+}
+
+//
+std::string
+Kumu::PathBasename(const std::string& Path, char separator)
+{
+  PathCompList_t CList;
+  PathToComponents(Path, CList, separator);
+
+  if ( CList.empty() )
+    return "";
+
+  return CList.back();
+}
+
+//
+std::string
+Kumu::PathDirname(const std::string& Path, char separator)
+{
+  PathCompList_t CList;
+  bool is_absolute = PathIsAbsolute(Path, separator);
+  PathToComponents(Path, CList, separator);
+
+  if ( CList.empty() )
+    return is_absolute ? "/" : "";
+
+  CList.pop_back();
+
+  if ( is_absolute )
+    return ComponentsToAbsolutePath(CList, separator);
+
+  return ComponentsToPath(CList, separator);
+}
+
+//
+std::string
+Kumu::PathGetExtension(const std::string& Path)
+{
+  std::string Basename = PathBasename(Path);
+  const char* p = strrchr(Basename.c_str(), '.'); 
+
+  if ( p++ == 0 )
+    return "";
+
+  return p;
+}
+
+//
+std::string
+Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
+{
+  std::string Basename = PathBasename(Path);
+  const char* p = strrchr(Basename.c_str(), '.'); 
+
+  if ( p != 0 )
+    Basename = Basename.substr(0, p - Basename.c_str());
+
+  if ( Extension.empty() )
+    return Basename;
+
+  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,
+                 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
+{
+  PathList_t::const_iterator si;
+  for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
+    {
+      FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
+
+      if ( one_shot && ! FoundPaths.empty() )
+       break;
+    }
+
+  return FoundPaths;
+}
+
+//
+Kumu::PathList_t&
+Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
+                 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
+{
+  char name_buf[MaxFilePath];
+  DirScanner Dir;
+
+  if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
+    {
+      while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
+       {
+         if ( name_buf[0] == '.' ) continue; // no hidden files
+         std::string tmp_path = SearchDir + separator + name_buf;
+
+         if ( PathIsDirectory(tmp_path.c_str()) )
+           FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
+         
+         else if ( Pattern.Match(name_buf) )
+           {
+             FoundPaths.push_back(SearchDir + separator + name_buf);
+             if ( one_shot )
+               break;
+           }
+       }
+    }
+
+  return FoundPaths;
+}
+
+
+#ifndef KM_WIN32
+
+//
+Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
+{
+  int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
+
+  if ( result )
+    {
+      char buf[128];
+      regerror(result, &m_regex, buf, 128);
+      DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
+      regfree(&m_regex);
+    }
+}
+
+Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
+  m_regex = rhs.m_regex;
+}
+
+Kumu::PathMatchRegex::~PathMatchRegex() {
+  regfree(&m_regex);
+}
+
+bool
+Kumu::PathMatchRegex::Match(const std::string& s) const {
+  return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
+}
+
+
+
+//
+Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
+{
+  std::string regex; // convert glob to regex
+
+  for ( const char* p = glob.c_str(); *p != 0; p++ )
+    {
+      switch (*p)
+       {
+       case '.':  regex += "\\.";  break;
+       case '*':  regex += ".*";   break;
+       case '?':  regex += ".?";   break;
+       default:   regex += *p;
+       }
+    }
+  regex += '$';
+
+  int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
+
+  if ( result )
+    {
+      char buf[128];
+      regerror(result, &m_regex, buf, 128);
+      DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
+      regfree(&m_regex);
+    }
+}
+
+Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
+  m_regex = rhs.m_regex;
+}
+
+Kumu::PathMatchGlob::~PathMatchGlob() {
+  regfree(&m_regex);
+}
+
+bool
+Kumu::PathMatchGlob::Match(const std::string& s) const {
+  return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
+}
+
+#endif
+
 //------------------------------------------------------------------------------------------
 // portable aspects of the file classes
 
@@ -175,7 +643,7 @@ Kumu::FileReader::Size() const
 
   if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
     {
-      if ( info.st_mode & S_IFREG )
+      if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
         return(info.st_size);
     }
 #endif
@@ -194,13 +662,13 @@ Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
 {
   assert( ! m_IOVec.empty() );
   register h__iovec* iov = m_IOVec;
-  KM_TEST_NULL(buf);
+  KM_TEST_NULL_L(buf);
 
   if ( iov->m_Count >= IOVecMaxEntries )
     {
-      DefaultLogSink().Error("The iovec is full! Only %lu entries allowed before a flush.\n",
+      DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
                             IOVecMaxEntries);
-      return RESULT_FAIL;
+      return RESULT_WRITEFAIL;
     }
 
   iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
@@ -216,15 +684,14 @@ Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
 //
 
 Kumu::Result_t
-Kumu::FileReader::OpenRead(const char* filename) const
+Kumu::FileReader::OpenRead(const std::string& filename) const
 {
-  KM_TEST_NULL_STR(filename);
   const_cast<FileReader*>(this)->m_Filename = filename;
   
   // 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 = ::CreateFileA(filename,
                          (GENERIC_READ),                // open for reading
                          FILE_SHARE_READ,               // share for reading
                          NULL,                          // no security
@@ -281,9 +748,9 @@ Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
 Kumu::Result_t
 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
 {
-  KM_TEST_NULL(pos);
+  KM_TEST_NULL_L(pos);
 
-  if ( m_Handle == (HANDLE)-1L )
+  if ( m_Handle == INVALID_HANDLE_VALUE )
     return Kumu::RESULT_FILEOPEN;
 
   LARGE_INTEGER in;
@@ -306,7 +773,7 @@ Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
 Kumu::Result_t
 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
 {
-  KM_TEST_NULL(buf);
+  KM_TEST_NULL_L(buf);
   Result_t result = Kumu::RESULT_OK;
   DWORD    tmp_count;
   ui32_t tmp_int;
@@ -341,15 +808,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(filename);
   m_Filename = filename;
   
   // suppress popup window on error
   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
 
-  m_Handle = ::CreateFile(filename,
+  m_Handle = ::CreateFileA(filename.c_str(),
                          (GENERIC_WRITE|GENERIC_READ),  // open for reading
                          FILE_SHARE_READ,               // share for reading
                          NULL,                          // no security
@@ -395,13 +861,12 @@ Kumu::FileWriter::Writev(ui32_t* bytes_written)
                                   (DWORD*)&tmp_count,
                                   NULL);
 
-      if ( wr_result == 0 )
+      if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
        {
          result = Kumu::RESULT_WRITEFAIL;
          break;
        }
 
-      assert(iov->m_iovec[i].iov_len == tmp_count);
       *bytes_written += tmp_count;
     }
 
@@ -415,7 +880,7 @@ Kumu::FileWriter::Writev(ui32_t* bytes_written)
 Kumu::Result_t
 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
 {
-  KM_TEST_NULL(buf);
+  KM_TEST_NULL_L(buf);
   ui32_t tmp_int;
 
   if ( bytes_written == 0 )
@@ -429,7 +894,10 @@ Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written
   BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
   ::SetErrorMode(prev);
 
-  return ( result == 0 ) ? Kumu::RESULT_WRITEFAIL : Kumu::RESULT_OK;
+  if ( result == 0 || *bytes_written != buf_len )
+    return Kumu::RESULT_WRITEFAIL;
+
+  return Kumu::RESULT_OK;
 }
 
 #else // KM_WIN32
@@ -438,11 +906,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(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;
 }
 
@@ -475,7 +942,7 @@ Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
 Kumu::Result_t
 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
 {
-  KM_TEST_NULL(pos);
+  KM_TEST_NULL_L(pos);
 
   if ( m_Handle == -1L )
     return RESULT_FILEOPEN;
@@ -493,7 +960,7 @@ Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
 Kumu::Result_t
 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
 {
-  KM_TEST_NULL(buf);
+  KM_TEST_NULL_L(buf);
   i32_t  tmp_count = 0;
   ui32_t tmp_int = 0;
 
@@ -518,15 +985,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(filename);
   m_Filename = filename;
-  m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
+  m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
 
   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;
     }
 
@@ -536,15 +1002,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(filename);
   m_Filename = filename;
-  m_Handle = open(filename, O_RDWR|O_CREAT, 0644);
+  m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
 
   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;
     }
 
@@ -566,13 +1031,17 @@ Kumu::FileWriter::Writev(ui32_t* bytes_written)
   if ( m_Handle == -1L )
     return RESULT_STATE;
 
-  int read_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
+  int total_size = 0;
+  for ( int i = 0; i < iov->m_Count; i++ )
+    total_size += iov->m_iovec[i].iov_len;
+
+  int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
   
-  if ( read_size == -1L )
+  if ( write_size == -1L || write_size != total_size )
     return RESULT_WRITEFAIL;
 
   iov->m_Count = 0;
-  *bytes_written = read_size;  
+  *bytes_written = write_size;  
   return RESULT_OK;
 }
 
@@ -580,24 +1049,21 @@ Kumu::FileWriter::Writev(ui32_t* bytes_written)
 Kumu::Result_t
 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
 {
-  KM_TEST_NULL(buf);
+  KM_TEST_NULL_L(buf);
   ui32_t tmp_int;
 
   if ( bytes_written == 0 )
     bytes_written = &tmp_int;
 
-  // TODO: flush iovec
-
-
   if ( m_Handle == -1L )
     return RESULT_STATE;
 
-  int read_size = write(m_Handle, buf, buf_len);
-  
-  if ( read_size == -1L )
+  int write_size = write(m_Handle, buf, buf_len);
+
+  if ( write_size == -1L || (ui32_t)write_size != buf_len )
     return RESULT_WRITEFAIL;
 
-  *bytes_written = read_size;  
+  *bytes_written = write_size;
   return RESULT_OK;
 }
 
@@ -609,23 +1075,30 @@ 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;
+  ui32_t     read_size = 0;
   FileReader File;
   ByteString ReadBuf;
 
-  KM_TEST_NULL_STR(filename);
-
   Result_t result = File.OpenRead(filename);
 
   if ( KM_SUCCESS(result) )
     {
       fsize = File.Size();
 
-      if ( fsize > max_size )
-       return RESULT_ALLOC;
+      if ( fsize > (Kumu::fpos_t)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.c_str());
+         return RESULT_READFAIL;
+       }
 
       result = ReadBuf.Capacity((ui32_t)fsize);
     }
@@ -640,28 +1113,156 @@ Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t ma
 }
 
 
+//
+Kumu::Result_t
+Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
+{
+  FileWriter File;
+  ui32_t write_count = 0;
+
+  Result_t result = File.OpenWrite(filename);
+
+  if ( KM_SUCCESS(result) )
+    result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
+
+  return result;
+}
+
+//------------------------------------------------------------------------------------------
+
+
+//
+Kumu::Result_t
+Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
+{
+  ByteString Buffer;
+  ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
+  Result_t result = Buffer.Capacity(file_size);
+
+  if ( KM_SUCCESS(result) )
+    {
+      ui32_t read_count = 0;
+      FileWriter Reader;
+
+      result = Reader.OpenRead(Filename);
+
+      if ( KM_SUCCESS(result) )
+       result = Reader.Read(Buffer.Data(), file_size, &read_count);
+    
+      if ( KM_SUCCESS(result) )
+       {
+         assert(file_size == read_count);
+         Buffer.Length(read_count);
+         MemIOReader MemReader(&Buffer);
+         result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
+       }
+    }
+
+  return result;
+}
+
+//
+Kumu::Result_t
+Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
+{
+  ByteString Buffer;
+  Result_t result = Buffer.Capacity(Object.ArchiveLength());
+
+  if ( KM_SUCCESS(result) )
+    {
+      ui32_t write_count = 0;
+      FileWriter Writer;
+      MemIOWriter MemWriter(&Buffer);
+
+      result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
+
+      if ( KM_SUCCESS(result) )
+       {
+         Buffer.Length(MemWriter.Length());
+         result = Writer.OpenWrite(Filename);
+       }
+
+      if ( KM_SUCCESS(result) )
+       result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
+    }
+
+  return result;
+}
+
 //------------------------------------------------------------------------------------------
 //
 
+//
+Result_t
+Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
+{
+  ui32_t file_size = FileSize(Filename);
+  Result_t result = Buffer.Capacity(file_size);
+
+  if ( KM_SUCCESS(result) )
+    {
+      ui32_t read_count = 0;
+      FileWriter Reader;
+
+      result = Reader.OpenRead(Filename);
+
+      if ( KM_SUCCESS(result) )
+       result = Reader.Read(Buffer.Data(), file_size, &read_count);
+    
+      if ( KM_SUCCESS(result) )
+       {
+         if ( file_size != read_count) 
+           return RESULT_READFAIL;
+
+         Buffer.Length(read_count);
+       }
+    }
+  
+  return result;
+}
+
+//
+Result_t
+Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
+{
+  ui32_t write_count = 0;
+  FileWriter Writer;
+
+  Result_t result = Writer.OpenWrite(Filename);
+
+  if ( KM_SUCCESS(result) )
+    result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
+
+  if ( KM_SUCCESS(result) && Buffer.Length() != write_count) 
+    return RESULT_WRITEFAIL;
+
+  return result;
+}
+
+//------------------------------------------------------------------------------------------
+//
+
+
 // Win32 directory scanner
 //
 #ifdef KM_WIN32
 
+//
+Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
+
 //
 //
 Result_t
-Kumu::DirScanner::Open(const char* filename)
+Kumu::DirScanner::Open(const std::string& filename)
 {
-  KM_TEST_NULL_STR(filename);
-
   // we need to append a '*' to read the entire directory
-  ui32_t fn_len = strlen(filename); 
+  ui32_t fn_len = filename.size(); 
   char* tmp_file = (char*)malloc(fn_len + 8);
 
   if ( tmp_file == 0 )
     return RESULT_ALLOC;
 
-  strcpy(tmp_file, filename);
+  strcpy(tmp_file, filename.c_str());
   char* p = &tmp_file[fn_len] - 1;
 
   if ( *p != '/' && *p != '\\' )
@@ -705,7 +1306,7 @@ Kumu::DirScanner::Close()
 Result_t
 Kumu::DirScanner::GetNext(char* filename)
 {
-  KM_TEST_NULL(filename);
+  KM_TEST_NULL_L(filename);
 
   if ( m_Handle == -1 )
     return RESULT_FILEOPEN;
@@ -732,21 +1333,34 @@ Kumu::DirScanner::GetNext(char* filename)
 
 // POSIX directory scanner
 
+//
+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(filename);
-
   Result_t result = RESULT_OK;
 
-  if ( ( m_Handle = opendir(filename) ) == NULL )
+  if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
     {
-      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;
+       }
     }
 
   return result;
@@ -760,8 +1374,17 @@ Kumu::DirScanner::Close()
   if ( m_Handle == NULL )
     return RESULT_FILEOPEN;
 
-  if ( closedir(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 = NULL;
   return RESULT_OK;
@@ -772,7 +1395,7 @@ Kumu::DirScanner::Close()
 Result_t
 Kumu::DirScanner::GetNext(char* filename)
 {
-  KM_TEST_NULL(filename);
+  KM_TEST_NULL_L(filename);
 
   if ( m_Handle == NULL )
     return RESULT_FILEOPEN;
@@ -795,6 +1418,191 @@ Kumu::DirScanner::GetNext(char* filename)
 #endif // KM_WIN32
 
 
+//------------------------------------------------------------------------------------------
+
+//
+// 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.
+//
+Result_t
+Kumu::CreateDirectoriesInPath(const std::string& Path)
+{
+  bool abs = PathIsAbsolute(Path);
+  PathCompList_t PathComps, TmpPathComps;
+
+  PathToComponents(Path, PathComps);
+
+  while ( ! PathComps.empty() )
+    {
+      TmpPathComps.push_back(PathComps.front());
+      PathComps.pop_front();
+      std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
+
+      if ( ! PathIsDirectory(tmp_path) )
+       {
+#ifdef KM_WIN32
+         if ( _mkdir(tmp_path.c_str()) != 0 )
+#else // KM_WIN32
+         if ( mkdir(tmp_path.c_str(), 0775) != 0 )
+#endif // KM_WIN32
+           {
+             DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
+                                    tmp_path.c_str(), strerror(errno));
+             return RESULT_DIR_CREATE;
+           }
+       }
+    }
+
+  return RESULT_OK;
+}
+
+
+//
+Result_t
+Kumu::DeleteFile(const std::string& filename)
+{
+  if ( _unlink(filename.c_str()) == 0 )
+    return RESULT_OK;
+
+  switch ( errno )
+    {
+    case ENOENT:
+    case ENOTDIR: return RESULT_NOTAFILE;
+
+    case EROFS:
+    case EBUSY:
+    case EACCES:
+    case EPERM:   return RESULT_NO_PERM;
+    }
+
+  DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
+  return RESULT_FAIL;
+}
+
+//
+Result_t
+h__DeletePath(const std::string& pathname)
+{
+  if ( pathname.empty() )
+    return RESULT_NULL_STR;
+
+  Result_t result = RESULT_OK;
+
+  if ( ! PathIsDirectory(pathname) )
+    {
+      result = DeleteFile(pathname);
+    }
+  else
+    {
+      {
+       DirScanner TestDir;
+       char       next_file[Kumu::MaxFilePath];
+       result = TestDir.Open(pathname.c_str());
+
+       while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
+         {
+           if ( next_file[0] == '.' )
+             {
+               if ( next_file[1] ==  0 )
+                 continue; // don't delete 'this'
+               
+               if ( next_file[1] == '.' && next_file[2] ==  0 )
+                 continue; // don't delete 'this' parent
+             }
+
+           result = h__DeletePath(pathname + std::string("/") + next_file);
+         }
+      }
+
+      if ( _rmdir(pathname.c_str()) != 0 )
+       {
+         switch ( errno )
+           {
+           case ENOENT:
+           case ENOTDIR:
+             result = RESULT_NOTAFILE;
+             break;
+
+           case EROFS:
+           case EBUSY:
+           case EACCES:
+           case EPERM:
+             result = RESULT_NO_PERM;
+             break;
+
+           default:
+             DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
+             result = RESULT_FAIL;
+           }
+       }
+    }
+
+  return result;
+}
+
+//
+Result_t
+Kumu::DeletePath(const std::string& 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::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 LastError = ::GetLastError();
+
+       DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
+       return RESULT_FAIL;
+#else // KM_WIN32
+  struct statfs s;
+
+  if ( statfs(path.c_str(), &s) == 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_bavail;
+      total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
+      return RESULT_OK;
+    }
+
+  switch ( errno )
+    {
+    case ENOENT:
+    case ENOTDIR: return RESULT_NOTAFILE;
+    case EACCES:  return RESULT_NO_PERM;
+    }
+
+  DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
+  return RESULT_FAIL;
+#endif // KM_WIN32
+} 
+
 
 //
 // end KM_fileio.cpp