out!
[asdcplib.git] / src / KM_fileio.cpp
index 61b7c88591ddc8ee5e2cefb4af77c24c954bd4f6..b9257abda88d7d9c87bd929cbce7a9bd806c47c7 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2007, John Hurst
+Copyright (c) 2004-2009, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -32,7 +32,9 @@ 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>
 #endif
@@ -52,6 +54,14 @@ struct iovec {
   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
@@ -113,7 +123,7 @@ 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_L(stat_info);
 
@@ -203,7 +213,7 @@ Kumu::FileSize(const std::string& pathname)
 
 //
 static PathCompList_t&
-s_PathMakeCanonical(PathCompList_t& CList, char separator, bool is_absolute)
+s_PathMakeCanonical(PathCompList_t& CList, bool is_absolute)
 {
   PathCompList_t::iterator ci, ri; // component and removal iterators
 
@@ -237,7 +247,7 @@ Kumu::PathMakeCanonical(const std::string& Path, char separator)
 {
   PathCompList_t CList;
   bool is_absolute = PathIsAbsolute(Path, separator);
-  s_PathMakeCanonical(PathToComponents(Path, CList, separator), separator, is_absolute);
+  s_PathMakeCanonical(PathToComponents(Path, CList, separator), is_absolute);
 
   if ( is_absolute )
     return ComponentsToAbsolutePath(CList, separator);
@@ -340,10 +350,10 @@ Kumu::PathMakeAbsolute(const std::string& Path, char separator)
     }
 
   PathCompList_t CList;
-  CList.push_back(cwd_buf);
+  PathToComponents(cwd_buf, CList);
   CList.push_back(Path);
 
-  return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, separator, true), separator);
+  return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, true), separator);
 }
 
 //
@@ -419,6 +429,28 @@ 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;
+}
+
 //
 Kumu::PathList_t&
 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
@@ -676,7 +708,7 @@ Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
 {
   KM_TEST_NULL_L(pos);
 
-  if ( m_Handle == (HANDLE)-1L )
+  if ( m_Handle == INVALID_HANDLE_VALUE )
     return Kumu::RESULT_FILEOPEN;
 
   LARGE_INTEGER in;
@@ -1061,14 +1093,128 @@ Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
   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.c_str());
+
+      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.c_str());
+       }
+
+      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.c_str());
+
+      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.c_str());
+
+  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
@@ -1154,6 +1300,9 @@ Kumu::DirScanner::GetNext(char* filename)
 
 // POSIX directory scanner
 
+//
+Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
+
 //
 Result_t
 Kumu::DirScanner::Open(const char* filename)
@@ -1164,11 +1313,23 @@ Kumu::DirScanner::Open(const char* filename)
 
   if ( ( m_Handle = opendir(filename) ) == 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", filename, strerror(errno));
+         result = RESULT_FAIL;
+       }
     }
 
   return result;
@@ -1182,8 +1343,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;
@@ -1217,6 +1387,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 = PathMakeAbsolute(PathMakeCanonical(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 = ::GetDiskFreeSpaceEx(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