Add PCMParserList.h to list of extra exported headers.
[asdcplib.git] / src / KM_fileio.cpp
index ec68f3804b78f206d23f917bf42a40cbdb4a7b1a..5a56c44cd09890c8831f4f4500e59c9e651f8844 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2006, John Hurst
+Copyright (c) 2004-2007, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <KM_log.h>
 #include <fcntl.h>
 #include <assert.h>
+#ifdef KM_WIN32
+#include <direct.h>
+#endif
 
 using namespace Kumu;
 
@@ -53,6 +56,33 @@ 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;
+         assert(r - pstr < 100);
+         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));
+}
+
+
 //
 static Kumu::Result_t
 do_stat(const char* path, fstat_t* stat_info)
@@ -104,14 +134,29 @@ do_fstat(HANDLE handle, fstat_t* stat_info)
 
 //
 bool
-Kumu::PathIsFile(const char* pathname)
+Kumu::PathExists(const std::string& pathname)
 {
-  if ( pathname == 0 || *pathname == 0 )
+  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|S_IFLNK ) )
         return true;
@@ -123,14 +168,14 @@ Kumu::PathIsFile(const char* pathname)
 
 //
 bool
-Kumu::PathIsDirectory(const char* pathname)
+Kumu::PathIsDirectory(const std::string& pathname)
 {
-  if ( pathname == 0 || *pathname == 0 )
+  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;
@@ -139,17 +184,16 @@ Kumu::PathIsDirectory(const char* pathname)
   return false;
 }
 
-
 //
 Kumu::fsize_t
-Kumu::FileSize(const char* pathname)
+Kumu::FileSize(const std::string& pathname)
 {
-  if ( pathname == 0 || *pathname == 0 )
-    return false;
+  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|S_IFLNK ) )
         return(info.st_size);
@@ -158,6 +202,346 @@ Kumu::FileSize(const char* pathname)
   return 0;
 }
 
+//
+static PathCompList_t&
+s_PathMakeCanonical(PathCompList_t& CList, char separator, bool is_absolute)
+{
+  PathCompList_t::iterator ci, ri; // component and removal iterators
+
+  for ( ci = CList.begin(); ci != CList.end(); ci++ )
+    {
+      if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
+        {
+          ri = ci++;
+          CList.erase(ri);
+        }
+      else if ( *ci == ".." && ci != CList.begin() )
+       {
+         ri = ci;
+         ri--;
+             
+         if ( *ri != ".." )
+           {
+             CList.erase(ri);
+             ri = ci++;
+             CList.erase(ri);
+            }
+        }
+    }
+
+  return CList;
+}
+
+//
+std::string
+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);
+
+  if ( is_absolute )
+    return ComponentsToAbsolutePath(CList, separator);
+
+  return ComponentsToPath(CList, separator);
+}
+
+//
+bool
+Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
+{
+  return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
+}
+
+//
+Kumu::PathCompList_t&
+Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
+{
+  split(Path, separator, CList);
+  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::PathMakeAbsolute(const std::string& Path, char separator)
+{
+  if ( Path.empty() )
+    {
+      std::string out_path;
+      out_path = separator;
+      return out_path;
+    }
+
+  if ( PathIsAbsolute(Path, separator) )
+    return Path;
+
+  char cwd_buf [MaxFilePath];
+  if ( getcwd(cwd_buf, MaxFilePath) == 0 )
+    {
+      DefaultLogSink().Error("Error retrieving current working directory.");
+      return "";
+    }
+
+  PathCompList_t CList;
+  CList.push_back(cwd_buf);
+  CList.push_back(Path);
+
+  return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, separator, true), separator);
+}
+
+//
+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;
+}
+
+//
+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) {
+  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) {
+  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
 
@@ -210,7 +594,7 @@ Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
     {
       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*
@@ -405,13 +789,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;
     }
 
@@ -439,7 +822,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
@@ -576,13 +962,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;
 }
 
@@ -596,18 +986,15 @@ Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written
   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;
 }
 
@@ -643,7 +1030,7 @@ Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t ma
       if ( fsize == 0 )
        {
          DefaultLogSink().Error("%s: zero file size\n", filename);
-         return RESULT_ALLOC;
+         return RESULT_READFAIL;
        }
 
       result = ReadBuf.Capacity((ui32_t)fsize);
@@ -672,10 +1059,7 @@ Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
   if ( KM_SUCCESS(result) )
     result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
 
-  if ( KM_SUCCESS(result) && write_count != inString.length() )
-    return RESULT_WRITEFAIL;
-
-  return RESULT_OK;
+  return result;
 }