Release me
[asdcplib.git] / src / KM_fileio.cpp
index 5149d3524fec598f4162c2087dd2e92a94765499..07c649fecc91343cb1d8ff0c317a3d5b61a6e54e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2014, John Hurst
+Copyright (c) 2004-2016, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -43,6 +43,15 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #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;
 
 #ifdef KM_WIN32
@@ -70,6 +79,9 @@ struct iovec {
 typedef struct stat     fstat_t;
 #endif
 
+#if defined(__sun) && defined(__SVR4)
+#include <sys/statfs.h>
+#endif
 
 //
 static Kumu::Result_t
@@ -626,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
 
@@ -690,6 +762,55 @@ Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
 
 
 #ifdef KM_WIN32
+#ifdef KM_WIN32_UTF8
+
+//
+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;
+}
+
+#endif // KM_WIN32_UTF8
+
 //------------------------------------------------------------------------------------------
 //
 
@@ -697,11 +818,25 @@ Kumu::Result_t
 Kumu::FileReader::OpenRead(const std::string& filename) const
 {
   const_cast<FileReader*>(this)->m_Filename = filename;
-  
+#ifdef KM_WIN32_UTF8
+  ByteString wb_filename;
+  Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
+
+  if ( KM_FAILURE(result) )
+    {
+      return result;
+    }
+#endif
+
   // suppress popup window on error
   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
 
+#ifdef KM_WIN32_UTF8
+  const_cast<FileReader*>(this)->m_Handle =
+            ::CreateFileW((wchar_t*)wb_filename.RoData(),
+#else
   const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename.c_str(),
+#endif
                          (GENERIC_READ),                // open for reading
                          FILE_SHARE_READ,               // share for reading
                          NULL,                          // no security
@@ -816,16 +951,30 @@ Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
 //------------------------------------------------------------------------------------------
 //
 
+
 //
 Kumu::Result_t
 Kumu::FileWriter::OpenWrite(const std::string& filename)
 {
   m_Filename = filename;
-  
+#ifdef KM_WIN32_UTF8
+  ByteString wb_filename;
+  Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
+
+  if ( KM_FAILURE(result) )
+    {
+      return result;
+    }
+#endif
+
   // suppress popup window on error
   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
 
+#ifdef KM_WIN32_UTF8
+  m_Handle = ::CreateFileW((wchar_t*)wb_filename.RoData(),
+#else
   m_Handle = ::CreateFileA(filename.c_str(),
+#endif
                          (GENERIC_WRITE|GENERIC_READ),  // open for reading
                          FILE_SHARE_READ,               // share for reading
                          NULL,                          // no security
@@ -998,7 +1147,7 @@ Kumu::Result_t
 Kumu::FileWriter::OpenWrite(const std::string& filename)
 {
   m_Filename = filename;
-  m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
+  m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0666);
 
   if ( m_Handle == -1L )
     {
@@ -1015,7 +1164,7 @@ Kumu::Result_t
 Kumu::FileWriter::OpenModify(const std::string& filename)
 {
   m_Filename = filename;
-  m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
+  m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0666);
 
   if ( m_Handle == -1L )
     {
@@ -1152,7 +1301,7 @@ 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);
 
@@ -1212,7 +1361,7 @@ 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);
 
@@ -1253,96 +1402,6 @@ Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Fil
 //
 
 
-// Win32 directory scanner
-//
-#ifdef KM_WIN32
-
-//
-Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
-
-//
-//
-Result_t
-Kumu::DirScanner::Open(const std::string& filename)
-{
-  // we need to append a '*' to read the entire directory
-  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.c_str());
-  char* p = &tmp_file[fn_len] - 1;
-
-  if ( *p != '/' && *p != '\\' )
-    {
-      p++;
-      *p++ = '/';
-    }
-
-  *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 )
-    return RESULT_FILEOPEN;
-
-  if ( _findclose((long)m_Handle) == -1 )
-    return RESULT_FAIL;
-
-  m_Handle = -1;
-  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 )
-    return RESULT_FILEOPEN;
-
-  if ( m_FileInfo.name[0] == '\0' )
-    return RESULT_ENDOFFILE;
-
-  strncpy(filename, m_FileInfo.name, MaxFilePath);
-  Result_t result = RESULT_OK;
-
-  if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
-    {
-      m_FileInfo.name[0] = '\0';
-         
-      if ( errno != ENOENT )
-       result = RESULT_FAIL;
-    }
-
-  return result;
-}
-
-
-#else // KM_WIN32
-
-// POSIX directory scanner
-
 //
 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
 
@@ -1424,6 +1483,7 @@ Kumu::DirScanner::GetNext(char* filename)
   return RESULT_OK;
 }
 
+//------------------------------------------------------------------------------------------
 
 //
 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
@@ -1495,6 +1555,9 @@ Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& n
   if ( m_Handle == 0 )
     return RESULT_FILEOPEN;
 
+#if defined(__sun) && defined(__SVR4)
+  struct stat s;
+#endif
   struct dirent* entry;
 
   for (;;)
@@ -1507,6 +1570,28 @@ Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& n
 
   next_item_name.assign(entry->d_name, strlen(entry->d_name));
 
+#if defined(__sun) && defined(__SVR4)
+
+  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:
@@ -1524,14 +1609,11 @@ Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& n
     default:
       next_item_type = DET_DEV;
     }
-
+#endif // __sun
   return RESULT_OK;
 }
 
 
-#endif // KM_WIN32
-
-
 //------------------------------------------------------------------------------------------
 
 //
@@ -1560,7 +1642,7 @@ Kumu::CreateDirectoriesInPath(const std::string& Path)
 #ifdef KM_WIN32
          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",
@@ -1718,6 +1800,20 @@ Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu:
 #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 )
@@ -1741,6 +1837,7 @@ 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
 }