2 Copyright (c) 2004-2014, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file KM_fileio.cpp
29 \brief portable file i/o
32 #include <asdcp/KM_fileio.h>
33 #include <asdcp/KM_log.h>
44 #define _getcwd getcwd
45 #define _unlink unlink
49 // only needed by GetExecutablePath()
50 #if defined(KM_MACOSX)
51 #include <mach-o/dyld.h>
57 typedef struct _stati64 fstat_t;
61 // win32 has WriteFileGather() and ReadFileScatter() but they
62 // demand page alignment and page sizing, making them unsuitable
63 // for use with arbitrary buffer sizes.
65 char* iov_base; // stupid iovec uses char*
69 # if defined(__linux__)
70 # include <sys/statfs.h>
72 # include <sys/param.h>
73 # include <sys/mount.h>
78 typedef struct stat fstat_t;
84 do_stat(const char* path, fstat_t* stat_info)
86 KM_TEST_NULL_STR_L(path);
87 KM_TEST_NULL_L(stat_info);
89 Kumu::Result_t result = Kumu::RESULT_OK;
92 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
94 int const wn = MultiByteToWideChar (CP_UTF8, 0, path, -1, 0, 0);
95 wchar_t* buffer = new wchar_t[wn];
96 if (MultiByteToWideChar (CP_UTF8, 0, path, -1, buffer, wn) == 0)
99 return Kumu::RESULT_FAIL;
102 if ( _wstati64(buffer, stat_info) == (__int64)-1 )
103 result = Kumu::RESULT_FILEOPEN;
107 ::SetErrorMode( prev );
109 if ( stat(path, stat_info) == -1L )
110 result = Kumu::RESULT_FILEOPEN;
112 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
113 result = Kumu::RESULT_FILEOPEN;
122 static Kumu::Result_t
123 do_fstat(FileHandle handle, fstat_t* stat_info)
125 KM_TEST_NULL_L(stat_info);
127 Kumu::Result_t result = Kumu::RESULT_OK;
129 if ( fstat(handle, stat_info) == -1L )
130 result = Kumu::RESULT_FILEOPEN;
132 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
133 result = Kumu::RESULT_FILEOPEN;
143 Kumu::PathExists(const std::string& pathname)
145 if ( pathname.empty() )
150 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
158 Kumu::PathIsFile(const std::string& pathname)
160 if ( pathname.empty() )
165 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
167 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
177 Kumu::PathIsDirectory(const std::string& pathname)
179 if ( pathname.empty() )
184 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
186 if ( info.st_mode & S_IFDIR )
195 Kumu::FileSize(const std::string& pathname)
197 if ( pathname.empty() )
202 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
204 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
205 return(info.st_size);
213 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
215 PathCompList_t::const_iterator i;
216 for ( i = in_list.begin(); i != in_list.end(); ++i )
220 if ( ! out_list.empty() )
225 else if ( *i != "." )
227 out_list.push_back(*i);
234 Kumu::PathMakeCanonical(const std::string& Path, char separator)
236 PathCompList_t in_list, out_list;
237 bool is_absolute = PathIsAbsolute(Path, separator);
238 PathToComponents(Path, in_list, separator);
239 make_canonical_list(in_list, out_list);
242 return ComponentsToAbsolutePath(out_list, separator);
244 return ComponentsToPath(out_list, separator);
249 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
251 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
255 Kumu::PathCompList_t&
256 Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
260 PathCompList_t tmp_list = km_token_split(path, std::string(s));
261 PathCompList_t::const_iterator i;
263 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
267 component_list.push_back(*i);
271 return component_list;
276 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
281 PathCompList_t::const_iterator ci = CList.begin();
282 std::string out_path = *ci;
284 for ( ci++; ci != CList.end(); ci++ )
285 out_path += separator + *ci;
292 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
294 std::string out_path;
297 out_path = separator;
300 PathCompList_t::const_iterator ci;
302 for ( ci = CList.begin(); ci != CList.end(); ci++ )
303 out_path += separator + *ci;
311 Kumu::PathHasComponents(const std::string& Path, char separator)
313 if ( strchr(Path.c_str(), separator) == 0 )
321 Kumu::PathIsAbsolute(const std::string& Path, char separator)
326 if ( Path[0] == separator)
336 char cwd_buf [MaxFilePath];
337 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
339 DefaultLogSink().Error("Error retrieving current working directory.");
348 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
357 if ( PathIsAbsolute(Path, separator) )
358 return PathMakeCanonical(Path);
360 PathCompList_t in_list, out_list;
361 PathToComponents(PathJoin(PathCwd(), Path), in_list);
362 make_canonical_list(in_list, out_list);
364 return ComponentsToAbsolutePath(out_list);
369 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
371 size_t pos = Path.find(Parent);
373 if ( pos == 0 ) // Parent found at offset 0
374 return Path.substr(Parent.size()+1);
381 Kumu::PathBasename(const std::string& Path, char separator)
383 PathCompList_t CList;
384 PathToComponents(Path, CList, separator);
394 Kumu::PathDirname(const std::string& Path, char separator)
396 PathCompList_t CList;
397 bool is_absolute = PathIsAbsolute(Path, separator);
398 PathToComponents(Path, CList, separator);
401 return is_absolute ? "/" : "";
406 return ComponentsToAbsolutePath(CList, separator);
408 return ComponentsToPath(CList, separator);
413 Kumu::PathGetExtension(const std::string& Path)
415 std::string Basename = PathBasename(Path);
416 const char* p = strrchr(Basename.c_str(), '.');
426 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
428 std::string Basename = PathBasename(Path);
429 const char* p = strrchr(Basename.c_str(), '.');
432 Basename = Basename.substr(0, p - Basename.c_str());
434 if ( Extension.empty() )
437 return Basename + "." + Extension;
442 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
444 return Path1 + separator + Path2;
449 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
451 return Path1 + separator + Path2 + separator + Path3;
456 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
457 const std::string& Path3, const std::string& Path4, char separator)
459 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
463 // returns false if link cannot be read
466 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
468 PathCompList_t in_list, out_list;
469 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
470 PathCompList_t::iterator i;
471 char link_buf[MaxFilePath];
473 for ( i = in_list.begin(); i != in_list.end(); ++i )
475 assert ( *i != ".." && *i != "." );
476 out_list.push_back(*i);
480 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
481 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
483 if ( link_size == -1 )
485 if ( errno == EINVAL )
488 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
492 assert(link_size < MaxFilePath);
493 link_buf[link_size] = 0;
494 std::string tmp_path;
497 if ( PathIsAbsolute(link_buf) )
503 tmp_path = PathJoin(PathDirname(next_link), link_buf);
506 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
510 resolved_path = ComponentsToAbsolutePath(out_list, separator);
515 // TODO: is there a reasonable equivalent to be written for win32?
518 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
520 resolved_path = link_path;
527 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
528 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
530 PathList_t::const_iterator si;
531 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
533 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
535 if ( one_shot && ! FoundPaths.empty() )
544 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
545 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
547 char name_buf[MaxFilePath];
550 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
552 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
554 if ( name_buf[0] == '.' ) continue; // no hidden files
555 std::string tmp_path = SearchDir + separator + name_buf;
557 if ( PathIsDirectory(tmp_path.c_str()) )
558 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
560 else if ( Pattern.Match(name_buf) )
562 FoundPaths.push_back(SearchDir + separator + name_buf);
576 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
578 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
583 regerror(result, &m_regex, buf, 128);
584 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
589 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
590 m_regex = rhs.m_regex;
593 Kumu::PathMatchRegex::~PathMatchRegex() {
598 Kumu::PathMatchRegex::Match(const std::string& s) const {
599 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
605 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
607 std::string regex; // convert glob to regex
609 for ( const char* p = glob.c_str(); *p != 0; p++ )
613 case '.': regex += "\\."; break;
614 case '*': regex += ".*"; break;
615 case '?': regex += ".?"; break;
616 default: regex += *p;
621 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
626 regerror(result, &m_regex, buf, 128);
627 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
632 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
633 m_regex = rhs.m_regex;
636 Kumu::PathMatchGlob::~PathMatchGlob() {
641 Kumu::PathMatchGlob::Match(const std::string& s) const {
642 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
648 //------------------------------------------------------------------------------------------
650 #define X_BUFSIZE 1024
654 Kumu::GetExecutablePath(const std::string& default_path)
656 char path[X_BUFSIZE] = {0};
657 bool success = false;
659 #if defined(KM_WIN32)
660 DWORD size = X_BUFSIZE;
661 DWORD rc = GetModuleFileName(0, path, size);
662 success = ( rc != 0 );
663 #elif defined(KM_MACOSX)
664 uint32_t size = X_BUFSIZE;
665 int rc = _NSGetExecutablePath(path, &size);
666 success = ( rc != -1 );
667 #elif defined(__linux__)
668 size_t size = X_BUFSIZE;
669 ssize_t rc = readlink("/proc/self/exe", path, size);
670 success = ( rc != -1 );
671 #elif defined(__OpenBSD__) || defined(__FreeBSD__)
672 size_t size = X_BUFSIZE;
673 ssize_t rc = readlink("/proc/curproc/file", path, size);
674 success = ( rc != -1 );
675 #elif defined(__FreeBSD__)
676 size_t size = X_BUFSIZE;
677 ssize_t rc = readlink("/proc/curproc/file", path, size);
678 success = ( rc != -1 );
679 #elif defined(__NetBSD__)
680 size_t size = X_BUFSIZE;
681 ssize_t rc = readlink("/proc/curproc/file", path, size);
682 success = ( rc != -1 );
684 #error GetExecutablePath --> Create a method for obtaining the executable name
689 return Kumu::PathMakeCanonical(path);
696 //------------------------------------------------------------------------------------------
697 // portable aspects of the file classes
699 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
702 class Kumu::FileWriter::h__iovec
706 struct iovec m_iovec[IOVecMaxEntries];
707 h__iovec() : m_Count(0) {}
714 Kumu::FileReader::Size() const
717 return FileSize(m_Filename.c_str());
721 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
723 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
724 return(info.st_size);
731 // these are declared here instead of in the header file
732 // because we have a mem_ptr that is managing a hidden class
733 Kumu::FileWriter::FileWriter()
736 Kumu::FileWriter::~FileWriter() {}
740 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
742 assert( ! m_IOVec.empty() );
743 h__iovec* iov = m_IOVec;
746 if ( iov->m_Count >= IOVecMaxEntries )
748 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
750 return RESULT_WRITEFAIL;
753 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
754 iov->m_iovec[iov->m_Count].iov_len = buf_len;
761 Kumu::FileWriter::StartHashing()
764 MD5_Init (&m_MD5Context);
768 Kumu::FileWriter::MaybeHash(void const * data, int size)
772 MD5_Update (&m_MD5Context, data, size);
777 Kumu::FileWriter::StopHashing()
781 unsigned char digest[MD5_DIGEST_LENGTH];
782 MD5_Final (digest, &m_MD5Context);
784 char hex[MD5_DIGEST_LENGTH * 2 + 1];
785 for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
787 sprintf(hex + i * 2, "%02x", digest[i]);
797 //------------------------------------------------------------------------------------------
801 Kumu::FileReader::OpenRead(const std::string& filename) const
803 const_cast<FileReader*>(this)->m_Filename = filename;
805 // suppress popup window on error
806 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
808 std::cout << "OpenRead " << filename << "\n";
810 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
811 wchar_t* buffer = new wchar_t[wn];
812 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0)
815 return Kumu::RESULT_FAIL;
818 std::wcout << "converted to " << buffer << "\n";
819 const_cast<FileReader*>(this)->m_Handle = ::CreateFileW(buffer,
820 (GENERIC_READ), // open for reading
821 FILE_SHARE_READ, // share for reading
823 OPEN_EXISTING, // read
824 FILE_ATTRIBUTE_NORMAL, // normal file
825 NULL // no template file
829 HRESULT const last_error = GetLastError();
831 ::SetErrorMode(prev);
833 if (m_Handle == INVALID_HANDLE_VALUE)
835 DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
836 return Kumu::RESULT_FILEOPEN;
839 return Kumu::RESULT_OK;
844 Kumu::FileReader::Close() const
846 if ( m_Handle == INVALID_HANDLE_VALUE )
847 return Kumu::RESULT_FILEOPEN;
849 // suppress popup window on error
850 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
851 BOOL result = ::CloseHandle(m_Handle);
852 ::SetErrorMode(prev);
853 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
855 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
860 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
862 if ( m_Handle == INVALID_HANDLE_VALUE )
863 return Kumu::RESULT_STATE;
866 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
867 in.QuadPart = position;
868 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
869 HRESULT LastError = GetLastError();
870 ::SetErrorMode(prev);
872 if ( (LastError != NO_ERROR
873 && (in.LowPart == INVALID_SET_FILE_POINTER
874 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
875 return Kumu::RESULT_READFAIL;
877 return Kumu::RESULT_OK;
882 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
886 if ( m_Handle == INVALID_HANDLE_VALUE )
887 return Kumu::RESULT_FILEOPEN;
890 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
891 in.QuadPart = (__int64)0;
892 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
893 HRESULT LastError = GetLastError();
894 ::SetErrorMode(prev);
896 if ( (LastError != NO_ERROR
897 && (in.LowPart == INVALID_SET_FILE_POINTER
898 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
899 return Kumu::RESULT_READFAIL;
901 *pos = (Kumu::fpos_t)in.QuadPart;
902 return Kumu::RESULT_OK;
907 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
910 Result_t result = Kumu::RESULT_OK;
914 if ( read_count == 0 )
915 read_count = &tmp_int;
919 if ( m_Handle == INVALID_HANDLE_VALUE )
920 return Kumu::RESULT_FILEOPEN;
922 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
923 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
924 result = Kumu::RESULT_READFAIL;
926 ::SetErrorMode(prev);
928 if ( tmp_count == 0 ) /* EOF */
929 result = Kumu::RESULT_ENDOFFILE;
931 if ( KM_SUCCESS(result) )
932 *read_count = tmp_count;
939 //------------------------------------------------------------------------------------------
944 Kumu::FileWriter::OpenWrite(const std::string& filename)
946 m_Filename = filename;
948 // suppress popup window on error
949 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
951 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
952 wchar_t* buffer = new wchar_t[wn];
953 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0)
956 return Kumu::RESULT_FAIL;
959 m_Handle = ::CreateFileW(buffer,
960 (GENERIC_WRITE|GENERIC_READ), // open for reading
961 FILE_SHARE_READ, // share for reading
963 CREATE_ALWAYS, // overwrite (beware!)
964 FILE_ATTRIBUTE_NORMAL, // normal file
965 NULL // no template file
969 HRESULT const last_error = GetLastError();
971 ::SetErrorMode(prev);
973 if (m_Handle == INVALID_HANDLE_VALUE)
975 DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
976 return Kumu::RESULT_FILEOPEN;
979 m_IOVec = new h__iovec;
980 return Kumu::RESULT_OK;
983 /** @param filename File name (UTF-8 encoded) */
985 Kumu::FileWriter::OpenModify(const std::string& filename)
987 m_Filename = filename;
989 // suppress popup window on error
990 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
992 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
993 wchar_t* buffer = new wchar_t[wn];
994 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0) {
996 return Kumu::RESULT_FAIL;
999 m_Handle = ::CreateFileW(buffer,
1000 (GENERIC_WRITE|GENERIC_READ), // open for reading
1001 FILE_SHARE_READ, // share for reading
1002 NULL, // no security
1003 OPEN_ALWAYS, // don't truncate existing
1004 FILE_ATTRIBUTE_NORMAL, // normal file
1005 NULL // no template file
1009 HRESULT const last_error = GetLastError();
1011 ::SetErrorMode(prev);
1013 if (m_Handle == INVALID_HANDLE_VALUE)
1015 DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
1016 return Kumu::RESULT_FILEOPEN;
1019 m_IOVec = new h__iovec;
1020 return Kumu::RESULT_OK;
1026 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1028 assert( ! m_IOVec.empty() );
1029 register h__iovec* iov = m_IOVec;
1032 if ( bytes_written == 0 )
1033 bytes_written = &tmp_int;
1035 if ( m_Handle == INVALID_HANDLE_VALUE )
1036 return Kumu::RESULT_STATE;
1039 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1040 Result_t result = Kumu::RESULT_OK;
1042 // AFAIK, there is no writev() equivalent in the win32 API
1043 for ( register int i = 0; i < iov->m_Count; i++ )
1045 ui32_t tmp_count = 0;
1046 BOOL wr_result = ::WriteFile(m_Handle,
1047 iov->m_iovec[i].iov_base,
1048 iov->m_iovec[i].iov_len,
1052 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1054 DefaultLogSink().Error("Writev failed (%d)", wr_result);
1055 result = Kumu::RESULT_WRITEFAIL;
1059 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1060 *bytes_written += tmp_count;
1063 ::SetErrorMode(prev);
1064 iov->m_Count = 0; // error nor not, all is lost
1071 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1073 KM_TEST_NULL_L(buf);
1076 if ( bytes_written == 0 )
1077 bytes_written = &tmp_int;
1079 if ( m_Handle == INVALID_HANDLE_VALUE )
1080 return Kumu::RESULT_STATE;
1082 // suppress popup window on error
1083 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1084 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1085 ::SetErrorMode(prev);
1087 if ( result == 0 || *bytes_written != buf_len )
1089 DefaultLogSink().Error("WriteFile failed (%d)", result);
1090 return Kumu::RESULT_WRITEFAIL;
1093 MaybeHash(buf, buf_len);
1095 return Kumu::RESULT_OK;
1099 //------------------------------------------------------------------------------------------
1104 Kumu::FileReader::OpenRead(const std::string& filename) const
1106 const_cast<FileReader*>(this)->m_Filename = filename;
1107 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1108 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1113 Kumu::FileReader::Close() const
1115 if ( m_Handle == -1L )
1116 return RESULT_FILEOPEN;
1119 const_cast<FileReader*>(this)->m_Handle = -1L;
1125 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1127 if ( m_Handle == -1L )
1128 return RESULT_FILEOPEN;
1130 if ( lseek(m_Handle, position, whence) == -1L )
1131 return RESULT_BADSEEK;
1138 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1140 KM_TEST_NULL_L(pos);
1142 if ( m_Handle == -1L )
1143 return RESULT_FILEOPEN;
1145 Kumu::fpos_t tmp_pos;
1147 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1148 return RESULT_READFAIL;
1156 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1158 KM_TEST_NULL_L(buf);
1159 i32_t tmp_count = 0;
1162 if ( read_count == 0 )
1163 read_count = &tmp_int;
1167 if ( m_Handle == -1L )
1168 return RESULT_FILEOPEN;
1170 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1171 return RESULT_READFAIL;
1173 *read_count = tmp_count;
1174 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1178 //------------------------------------------------------------------------------------------
1183 Kumu::FileWriter::OpenWrite(const std::string& filename)
1185 m_Filename = filename;
1186 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
1188 if ( m_Handle == -1L )
1190 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1191 return RESULT_FILEOPEN;
1194 m_IOVec = new h__iovec;
1200 Kumu::FileWriter::OpenModify(const std::string& filename)
1202 m_Filename = filename;
1203 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1205 if ( m_Handle == -1L )
1207 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1208 return RESULT_FILEOPEN;
1211 m_IOVec = new h__iovec;
1217 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1219 assert( ! m_IOVec.empty() );
1220 h__iovec* iov = m_IOVec;
1223 if ( bytes_written == 0 )
1224 bytes_written = &tmp_int;
1226 if ( m_Handle == -1L )
1227 return RESULT_STATE;
1230 for ( int i = 0; i < iov->m_Count; i++ )
1231 total_size += iov->m_iovec[i].iov_len;
1233 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1235 if ( write_size == -1L || write_size != total_size )
1237 DefaultLogSink().Error("writev failed (%d)", errno);
1238 return RESULT_WRITEFAIL;
1241 for (int i = 0; i < iov->m_Count; ++i)
1243 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1247 *bytes_written = write_size;
1253 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1255 KM_TEST_NULL_L(buf);
1258 if ( bytes_written == 0 )
1259 bytes_written = &tmp_int;
1261 if ( m_Handle == -1L )
1262 return RESULT_STATE;
1264 int write_size = write(m_Handle, buf, buf_len);
1265 MaybeHash(buf, buf_len);
1267 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1269 DefaultLogSink().Error("write failed (%d)", errno);
1270 return RESULT_WRITEFAIL;
1273 *bytes_written = write_size;
1280 //------------------------------------------------------------------------------------------
1285 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1288 ui32_t read_size = 0;
1292 Result_t result = File.OpenRead(filename);
1294 if ( KM_SUCCESS(result) )
1296 fsize = File.Size();
1298 if ( fsize > (Kumu::fpos_t)max_size )
1300 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1301 return RESULT_ALLOC;
1306 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1307 return RESULT_READFAIL;
1310 result = ReadBuf.Capacity((ui32_t)fsize);
1313 if ( KM_SUCCESS(result) )
1314 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1316 if ( KM_SUCCESS(result) )
1317 outString.assign((const char*)ReadBuf.RoData(), read_size);
1325 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1328 ui32_t write_count = 0;
1330 Result_t result = File.OpenWrite(filename);
1332 if ( KM_SUCCESS(result) )
1333 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1338 //------------------------------------------------------------------------------------------
1343 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1346 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1347 Result_t result = Buffer.Capacity(file_size);
1349 if ( KM_SUCCESS(result) )
1351 ui32_t read_count = 0;
1354 result = Reader.OpenRead(Filename);
1356 if ( KM_SUCCESS(result) )
1357 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1359 if ( KM_SUCCESS(result) )
1361 assert(file_size == read_count);
1362 Buffer.Length(read_count);
1363 MemIOReader MemReader(&Buffer);
1364 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1373 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1376 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1378 if ( KM_SUCCESS(result) )
1380 ui32_t write_count = 0;
1382 MemIOWriter MemWriter(&Buffer);
1384 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1386 if ( KM_SUCCESS(result) )
1388 Buffer.Length(MemWriter.Length());
1389 result = Writer.OpenWrite(Filename);
1393 DefaultLogSink().Error("Object.Archive() failed in WriteObjectIntoFile()");
1396 if ( KM_SUCCESS(result) )
1397 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1403 //------------------------------------------------------------------------------------------
1408 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1410 ui32_t file_size = FileSize(Filename);
1411 Result_t result = Buffer.Capacity(file_size);
1413 if ( KM_SUCCESS(result) )
1415 ui32_t read_count = 0;
1418 result = Reader.OpenRead(Filename);
1420 if ( KM_SUCCESS(result) )
1421 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1423 if ( KM_SUCCESS(result) )
1425 if ( file_size != read_count)
1426 return RESULT_READFAIL;
1428 Buffer.Length(read_count);
1437 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1439 ui32_t write_count = 0;
1442 Result_t result = Writer.OpenWrite(Filename);
1444 if ( KM_SUCCESS(result) )
1445 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1447 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1449 DefaultLogSink().Error("WriteBufferIntoFile failed (%d)", result.Value());
1450 return RESULT_WRITEFAIL;
1456 //------------------------------------------------------------------------------------------
1459 Kumu::DirScanner::DirScanner()
1460 : _iterator(boost::filesystem::directory_iterator())
1466 Kumu::DirScanner::Open(const std::string& filename)
1468 if (!boost::filesystem::is_directory(filename))
1469 return RESULT_NOT_FOUND;
1471 _iterator = boost::filesystem::directory_iterator(filename);
1478 Kumu::DirScanner::GetNext(char* filename)
1480 if (_iterator == boost::filesystem::directory_iterator())
1481 return RESULT_ENDOFFILE;
1483 #if BOOST_FILESYSTEM_VERSION == 3
1484 std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1486 std::string f = boost::filesystem::path(*_iterator).filename();
1488 strncpy(filename, f.c_str(), MaxFilePath);
1493 //------------------------------------------------------------------------------------------
1496 // Attention Windows users: make sure to use the proper separator character
1497 // with these functions.
1500 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1503 Kumu::CreateDirectoriesInPath(const std::string& Path)
1505 bool abs = PathIsAbsolute(Path);
1506 PathCompList_t PathComps, TmpPathComps;
1508 PathToComponents(Path, PathComps);
1510 while ( ! PathComps.empty() )
1512 TmpPathComps.push_back(PathComps.front());
1513 PathComps.pop_front();
1514 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1516 if ( ! PathIsDirectory(tmp_path) )
1519 if ( _mkdir(tmp_path.c_str()) != 0 )
1521 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1524 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1525 tmp_path.c_str(), strerror(errno));
1526 return RESULT_DIR_CREATE;
1537 Kumu::DeleteFile(const std::string& filename)
1539 if ( _unlink(filename.c_str()) == 0 )
1545 case ENOTDIR: return RESULT_NOTAFILE;
1550 case EPERM: return RESULT_NO_PERM;
1553 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1559 h__DeletePath(const std::string& pathname)
1561 if ( pathname.empty() )
1562 return RESULT_NULL_STR;
1564 Result_t result = RESULT_OK;
1566 if ( ! PathIsDirectory(pathname) )
1568 result = DeleteFile(pathname);
1574 char next_file[Kumu::MaxFilePath];
1575 result = TestDir.Open(pathname.c_str());
1577 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1579 if ( next_file[0] == '.' )
1581 if ( next_file[1] == 0 )
1582 continue; // don't delete 'this'
1584 if ( next_file[1] == '.' && next_file[2] == 0 )
1585 continue; // don't delete 'this' parent
1588 result = h__DeletePath(pathname + std::string("/") + next_file);
1592 if ( _rmdir(pathname.c_str()) != 0 )
1598 result = RESULT_NOTAFILE;
1605 result = RESULT_NO_PERM;
1609 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1610 result = RESULT_FAIL;
1620 Kumu::DeletePath(const std::string& pathname)
1622 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1623 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1624 return h__DeletePath(c_pathname);
1630 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1632 DirScanner source_dir;
1633 char next_file[Kumu::MaxFilePath];
1635 Result_t result = source_dir.Open(path);
1637 if ( KM_FAILURE(result) )
1640 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1642 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1643 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1646 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1649 return DeletePath(path);
1653 //------------------------------------------------------------------------------------------
1658 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1661 ULARGE_INTEGER lTotalNumberOfBytes;
1662 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1664 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1667 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1668 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1672 HRESULT last_error = ::GetLastError();
1674 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1679 if ( statfs(path.c_str(), &s) == 0 )
1681 if ( s.f_blocks < 1 )
1683 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1684 path.c_str(), s.f_blocks);
1688 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1689 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1696 case ENOTDIR: return RESULT_NOTAFILE;
1697 case EACCES: return RESULT_NO_PERM;
1700 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1707 // end KM_fileio.cpp