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>
42 #define _getcwd getcwd
43 #define _unlink unlink
47 // only needed by GetExecutablePath()
48 #if defined(KM_MACOSX)
49 #include <mach-o/dyld.h>
55 typedef struct _stati64 fstat_t;
59 // win32 has WriteFileGather() and ReadFileScatter() but they
60 // demand page alignment and page sizing, making them unsuitable
61 // for use with arbitrary buffer sizes.
63 char* iov_base; // stupid iovec uses char*
67 # if defined(__linux__)
68 # include <sys/statfs.h>
70 # include <sys/param.h>
71 # include <sys/mount.h>
76 typedef struct stat fstat_t;
82 do_stat(const char* path, fstat_t* stat_info)
84 KM_TEST_NULL_STR_L(path);
85 KM_TEST_NULL_L(stat_info);
87 Kumu::Result_t result = Kumu::RESULT_OK;
90 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
92 int const wn = MultiByteToWideChar (CP_UTF8, 0, path, -1, 0, 0);
93 wchar_t* buffer = new wchar_t[wn];
94 if (MultiByteToWideChar (CP_UTF8, 0, path, -1, buffer, wn) == 0)
97 return Kumu::RESULT_FAIL;
100 if ( _wstati64(buffer, stat_info) == (__int64)-1 )
101 result = Kumu::RESULT_FILEOPEN;
105 ::SetErrorMode( prev );
107 if ( stat(path, stat_info) == -1L )
108 result = Kumu::RESULT_FILEOPEN;
110 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
111 result = Kumu::RESULT_FILEOPEN;
120 static Kumu::Result_t
121 do_fstat(FileHandle handle, fstat_t* stat_info)
123 KM_TEST_NULL_L(stat_info);
125 Kumu::Result_t result = Kumu::RESULT_OK;
127 if ( fstat(handle, stat_info) == -1L )
128 result = Kumu::RESULT_FILEOPEN;
130 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
131 result = Kumu::RESULT_FILEOPEN;
141 Kumu::PathExists(const std::string& pathname)
143 if ( pathname.empty() )
148 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
156 Kumu::PathIsFile(const std::string& pathname)
158 if ( pathname.empty() )
163 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
165 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
175 Kumu::PathIsDirectory(const std::string& pathname)
177 if ( pathname.empty() )
182 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
184 if ( info.st_mode & S_IFDIR )
193 Kumu::FileSize(const std::string& pathname)
195 if ( pathname.empty() )
200 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
202 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
203 return(info.st_size);
211 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
213 PathCompList_t::const_iterator i;
214 for ( i = in_list.begin(); i != in_list.end(); ++i )
218 if ( ! out_list.empty() )
223 else if ( *i != "." )
225 out_list.push_back(*i);
232 Kumu::PathMakeCanonical(const std::string& Path, char separator)
234 PathCompList_t in_list, out_list;
235 bool is_absolute = PathIsAbsolute(Path, separator);
236 PathToComponents(Path, in_list, separator);
237 make_canonical_list(in_list, out_list);
240 return ComponentsToAbsolutePath(out_list, separator);
242 return ComponentsToPath(out_list, separator);
247 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
249 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
253 Kumu::PathCompList_t&
254 Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
258 PathCompList_t tmp_list = km_token_split(path, std::string(s));
259 PathCompList_t::const_iterator i;
261 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
265 component_list.push_back(*i);
269 return component_list;
274 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
279 PathCompList_t::const_iterator ci = CList.begin();
280 std::string out_path = *ci;
282 for ( ci++; ci != CList.end(); ci++ )
283 out_path += separator + *ci;
290 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
292 std::string out_path;
295 out_path = separator;
298 PathCompList_t::const_iterator ci;
300 for ( ci = CList.begin(); ci != CList.end(); ci++ )
301 out_path += separator + *ci;
309 Kumu::PathHasComponents(const std::string& Path, char separator)
311 if ( strchr(Path.c_str(), separator) == 0 )
319 Kumu::PathIsAbsolute(const std::string& Path, char separator)
324 if ( Path[0] == separator)
334 char cwd_buf [MaxFilePath];
335 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
337 DefaultLogSink().Error("Error retrieving current working directory.");
346 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
355 if ( PathIsAbsolute(Path, separator) )
356 return PathMakeCanonical(Path);
358 PathCompList_t in_list, out_list;
359 PathToComponents(PathJoin(PathCwd(), Path), in_list);
360 make_canonical_list(in_list, out_list);
362 return ComponentsToAbsolutePath(out_list);
367 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
369 size_t pos = Path.find(Parent);
371 if ( pos == 0 ) // Parent found at offset 0
372 return Path.substr(Parent.size()+1);
379 Kumu::PathBasename(const std::string& Path, char separator)
381 PathCompList_t CList;
382 PathToComponents(Path, CList, separator);
392 Kumu::PathDirname(const std::string& Path, char separator)
394 PathCompList_t CList;
395 bool is_absolute = PathIsAbsolute(Path, separator);
396 PathToComponents(Path, CList, separator);
399 return is_absolute ? "/" : "";
404 return ComponentsToAbsolutePath(CList, separator);
406 return ComponentsToPath(CList, separator);
411 Kumu::PathGetExtension(const std::string& Path)
413 std::string Basename = PathBasename(Path);
414 const char* p = strrchr(Basename.c_str(), '.');
424 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
426 std::string Basename = PathBasename(Path);
427 const char* p = strrchr(Basename.c_str(), '.');
430 Basename = Basename.substr(0, p - Basename.c_str());
432 if ( Extension.empty() )
435 return Basename + "." + Extension;
440 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
442 return Path1 + separator + Path2;
447 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
449 return Path1 + separator + Path2 + separator + Path3;
454 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
455 const std::string& Path3, const std::string& Path4, char separator)
457 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
461 // returns false if link cannot be read
464 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
466 PathCompList_t in_list, out_list;
467 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
468 PathCompList_t::iterator i;
469 char link_buf[MaxFilePath];
471 for ( i = in_list.begin(); i != in_list.end(); ++i )
473 assert ( *i != ".." && *i != "." );
474 out_list.push_back(*i);
478 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
479 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
481 if ( link_size == -1 )
483 if ( errno == EINVAL )
486 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
490 assert(link_size < MaxFilePath);
491 link_buf[link_size] = 0;
492 std::string tmp_path;
495 if ( PathIsAbsolute(link_buf) )
501 tmp_path = PathJoin(PathDirname(next_link), link_buf);
504 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
508 resolved_path = ComponentsToAbsolutePath(out_list, separator);
513 // TODO: is there a reasonable equivalent to be written for win32?
516 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
518 resolved_path = link_path;
525 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
526 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
528 PathList_t::const_iterator si;
529 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
531 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
533 if ( one_shot && ! FoundPaths.empty() )
542 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
543 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
545 char name_buf[MaxFilePath];
548 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
550 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
552 if ( name_buf[0] == '.' ) continue; // no hidden files
553 std::string tmp_path = SearchDir + separator + name_buf;
555 if ( PathIsDirectory(tmp_path.c_str()) )
556 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
558 else if ( Pattern.Match(name_buf) )
560 FoundPaths.push_back(SearchDir + separator + name_buf);
574 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
576 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
581 regerror(result, &m_regex, buf, 128);
582 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
587 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
588 m_regex = rhs.m_regex;
591 Kumu::PathMatchRegex::~PathMatchRegex() {
596 Kumu::PathMatchRegex::Match(const std::string& s) const {
597 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
603 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
605 std::string regex; // convert glob to regex
607 for ( const char* p = glob.c_str(); *p != 0; p++ )
611 case '.': regex += "\\."; break;
612 case '*': regex += ".*"; break;
613 case '?': regex += ".?"; break;
614 default: regex += *p;
619 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
624 regerror(result, &m_regex, buf, 128);
625 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
630 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
631 m_regex = rhs.m_regex;
634 Kumu::PathMatchGlob::~PathMatchGlob() {
639 Kumu::PathMatchGlob::Match(const std::string& s) const {
640 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
646 //------------------------------------------------------------------------------------------
648 #define X_BUFSIZE 1024
652 Kumu::GetExecutablePath(const std::string& default_path)
654 char path[X_BUFSIZE] = {0};
655 bool success = false;
657 #if defined(KM_WIN32)
658 DWORD size = X_BUFSIZE;
659 DWORD rc = GetModuleFileName(0, path, size);
660 success = ( rc != 0 );
661 #elif defined(KM_MACOSX)
662 uint32_t size = X_BUFSIZE;
663 int rc = _NSGetExecutablePath(path, &size);
664 success = ( rc != -1 );
665 #elif defined(__linux__)
666 size_t size = X_BUFSIZE;
667 ssize_t rc = readlink("/proc/self/exe", path, size);
668 success = ( rc != -1 );
669 #elif defined(__OpenBSD__) || defined(__FreeBSD__)
670 size_t size = X_BUFSIZE;
671 ssize_t rc = readlink("/proc/curproc/file", path, size);
672 success = ( rc != -1 );
673 #elif defined(__FreeBSD__)
674 size_t size = X_BUFSIZE;
675 ssize_t rc = readlink("/proc/curproc/file", path, size);
676 success = ( rc != -1 );
677 #elif defined(__NetBSD__)
678 size_t size = X_BUFSIZE;
679 ssize_t rc = readlink("/proc/curproc/file", path, size);
680 success = ( rc != -1 );
682 #error GetExecutablePath --> Create a method for obtaining the executable name
687 return Kumu::PathMakeCanonical(path);
694 //------------------------------------------------------------------------------------------
695 // portable aspects of the file classes
697 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
700 class Kumu::FileWriter::h__iovec
704 struct iovec m_iovec[IOVecMaxEntries];
705 h__iovec() : m_Count(0) {}
712 Kumu::FileReader::Size() const
715 return FileSize(m_Filename.c_str());
719 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
721 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
722 return(info.st_size);
729 // these are declared here instead of in the header file
730 // because we have a mem_ptr that is managing a hidden class
731 Kumu::FileWriter::FileWriter()
734 Kumu::FileWriter::~FileWriter() {}
738 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
740 assert( ! m_IOVec.empty() );
741 h__iovec* iov = m_IOVec;
744 if ( iov->m_Count >= IOVecMaxEntries )
746 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
748 return RESULT_WRITEFAIL;
751 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
752 iov->m_iovec[iov->m_Count].iov_len = buf_len;
759 Kumu::FileWriter::StartHashing()
762 MD5_Init (&m_MD5Context);
766 Kumu::FileWriter::MaybeHash(void const * data, int size)
770 MD5_Update (&m_MD5Context, data, size);
775 Kumu::FileWriter::StopHashing()
779 unsigned char digest[MD5_DIGEST_LENGTH];
780 MD5_Final (digest, &m_MD5Context);
782 char hex[MD5_DIGEST_LENGTH * 2 + 1];
783 for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
785 sprintf(hex + i * 2, "%02x", digest[i]);
795 //------------------------------------------------------------------------------------------
799 Kumu::FileReader::OpenRead(const std::string& filename) const
801 const_cast<FileReader*>(this)->m_Filename = filename;
803 // suppress popup window on error
804 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
806 std::cout << "OpenRead " << filename << "\n";
808 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
809 wchar_t* buffer = new wchar_t[wn];
810 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0)
813 return Kumu::RESULT_FAIL;
816 std::wcout << "converted to " << buffer << "\n";
817 const_cast<FileReader*>(this)->m_Handle = ::CreateFileW(buffer,
818 (GENERIC_READ), // open for reading
819 FILE_SHARE_READ, // share for reading
821 OPEN_EXISTING, // read
822 FILE_ATTRIBUTE_NORMAL, // normal file
823 NULL // no template file
827 HRESULT const last_error = GetLastError();
829 ::SetErrorMode(prev);
831 if (m_Handle == INVALID_HANDLE_VALUE)
833 DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
834 return Kumu::RESULT_FILEOPEN;
837 return Kumu::RESULT_OK;
842 Kumu::FileReader::Close() const
844 if ( m_Handle == INVALID_HANDLE_VALUE )
845 return Kumu::RESULT_FILEOPEN;
847 // suppress popup window on error
848 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
849 BOOL result = ::CloseHandle(m_Handle);
850 ::SetErrorMode(prev);
851 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
853 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
858 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
860 if ( m_Handle == INVALID_HANDLE_VALUE )
861 return Kumu::RESULT_STATE;
864 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
865 in.QuadPart = position;
866 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
867 HRESULT LastError = GetLastError();
868 ::SetErrorMode(prev);
870 if ( (LastError != NO_ERROR
871 && (in.LowPart == INVALID_SET_FILE_POINTER
872 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
873 return Kumu::RESULT_READFAIL;
875 return Kumu::RESULT_OK;
880 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
884 if ( m_Handle == INVALID_HANDLE_VALUE )
885 return Kumu::RESULT_FILEOPEN;
888 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
889 in.QuadPart = (__int64)0;
890 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
891 HRESULT LastError = GetLastError();
892 ::SetErrorMode(prev);
894 if ( (LastError != NO_ERROR
895 && (in.LowPart == INVALID_SET_FILE_POINTER
896 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
897 return Kumu::RESULT_READFAIL;
899 *pos = (Kumu::fpos_t)in.QuadPart;
900 return Kumu::RESULT_OK;
905 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
908 Result_t result = Kumu::RESULT_OK;
912 if ( read_count == 0 )
913 read_count = &tmp_int;
917 if ( m_Handle == INVALID_HANDLE_VALUE )
918 return Kumu::RESULT_FILEOPEN;
920 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
921 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
922 result = Kumu::RESULT_READFAIL;
924 ::SetErrorMode(prev);
926 if ( tmp_count == 0 ) /* EOF */
927 result = Kumu::RESULT_ENDOFFILE;
929 if ( KM_SUCCESS(result) )
930 *read_count = tmp_count;
937 //------------------------------------------------------------------------------------------
942 Kumu::FileWriter::OpenWrite(const std::string& filename)
944 m_Filename = filename;
946 // suppress popup window on error
947 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
949 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
950 wchar_t* buffer = new wchar_t[wn];
951 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0)
954 return Kumu::RESULT_FAIL;
957 m_Handle = ::CreateFileW(buffer,
958 (GENERIC_WRITE|GENERIC_READ), // open for reading
959 FILE_SHARE_READ, // share for reading
961 CREATE_ALWAYS, // overwrite (beware!)
962 FILE_ATTRIBUTE_NORMAL, // normal file
963 NULL // no template file
967 HRESULT const last_error = GetLastError();
969 ::SetErrorMode(prev);
971 if (m_Handle == INVALID_HANDLE_VALUE)
973 DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
974 return Kumu::RESULT_FILEOPEN;
977 m_IOVec = new h__iovec;
978 return Kumu::RESULT_OK;
981 /** @param filename File name (UTF-8 encoded) */
983 Kumu::FileWriter::OpenModify(const std::string& filename)
985 m_Filename = filename;
987 // suppress popup window on error
988 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
990 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
991 wchar_t* buffer = new wchar_t[wn];
992 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0) {
994 return Kumu::RESULT_FAIL;
997 m_Handle = ::CreateFileW(buffer,
998 (GENERIC_WRITE|GENERIC_READ), // open for reading
999 FILE_SHARE_READ, // share for reading
1000 NULL, // no security
1001 OPEN_ALWAYS, // don't truncate existing
1002 FILE_ATTRIBUTE_NORMAL, // normal file
1003 NULL // no template file
1007 HRESULT const last_error = GetLastError();
1009 ::SetErrorMode(prev);
1011 if (m_Handle == INVALID_HANDLE_VALUE)
1013 DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
1014 return Kumu::RESULT_FILEOPEN;
1017 m_IOVec = new h__iovec;
1018 return Kumu::RESULT_OK;
1024 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1026 assert( ! m_IOVec.empty() );
1027 register h__iovec* iov = m_IOVec;
1030 if ( bytes_written == 0 )
1031 bytes_written = &tmp_int;
1033 if ( m_Handle == INVALID_HANDLE_VALUE )
1034 return Kumu::RESULT_STATE;
1037 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1038 Result_t result = Kumu::RESULT_OK;
1040 // AFAIK, there is no writev() equivalent in the win32 API
1041 for ( register int i = 0; i < iov->m_Count; i++ )
1043 ui32_t tmp_count = 0;
1044 BOOL wr_result = ::WriteFile(m_Handle,
1045 iov->m_iovec[i].iov_base,
1046 iov->m_iovec[i].iov_len,
1050 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1052 DefaultLogSink().Error("Writev failed (%d)", wr_result);
1053 result = Kumu::RESULT_WRITEFAIL;
1057 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1058 *bytes_written += tmp_count;
1061 ::SetErrorMode(prev);
1062 iov->m_Count = 0; // error nor not, all is lost
1069 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1071 KM_TEST_NULL_L(buf);
1074 if ( bytes_written == 0 )
1075 bytes_written = &tmp_int;
1077 if ( m_Handle == INVALID_HANDLE_VALUE )
1078 return Kumu::RESULT_STATE;
1080 // suppress popup window on error
1081 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1082 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1083 ::SetErrorMode(prev);
1085 if ( result == 0 || *bytes_written != buf_len )
1087 DefaultLogSink().Error("WriteFile failed (%d)", result);
1088 return Kumu::RESULT_WRITEFAIL;
1091 MaybeHash(buf, buf_len);
1093 return Kumu::RESULT_OK;
1097 //------------------------------------------------------------------------------------------
1102 Kumu::FileReader::OpenRead(const std::string& filename) const
1104 const_cast<FileReader*>(this)->m_Filename = filename;
1105 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1106 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1111 Kumu::FileReader::Close() const
1113 if ( m_Handle == -1L )
1114 return RESULT_FILEOPEN;
1117 const_cast<FileReader*>(this)->m_Handle = -1L;
1123 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1125 if ( m_Handle == -1L )
1126 return RESULT_FILEOPEN;
1128 if ( lseek(m_Handle, position, whence) == -1L )
1129 return RESULT_BADSEEK;
1136 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1138 KM_TEST_NULL_L(pos);
1140 if ( m_Handle == -1L )
1141 return RESULT_FILEOPEN;
1143 Kumu::fpos_t tmp_pos;
1145 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1146 return RESULT_READFAIL;
1154 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1156 KM_TEST_NULL_L(buf);
1157 i32_t tmp_count = 0;
1160 if ( read_count == 0 )
1161 read_count = &tmp_int;
1165 if ( m_Handle == -1L )
1166 return RESULT_FILEOPEN;
1168 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1169 return RESULT_READFAIL;
1171 *read_count = tmp_count;
1172 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1176 //------------------------------------------------------------------------------------------
1181 Kumu::FileWriter::OpenWrite(const std::string& filename)
1183 m_Filename = filename;
1184 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
1186 if ( m_Handle == -1L )
1188 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1189 return RESULT_FILEOPEN;
1192 m_IOVec = new h__iovec;
1198 Kumu::FileWriter::OpenModify(const std::string& filename)
1200 m_Filename = filename;
1201 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1203 if ( m_Handle == -1L )
1205 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1206 return RESULT_FILEOPEN;
1209 m_IOVec = new h__iovec;
1215 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1217 assert( ! m_IOVec.empty() );
1218 h__iovec* iov = m_IOVec;
1221 if ( bytes_written == 0 )
1222 bytes_written = &tmp_int;
1224 if ( m_Handle == -1L )
1225 return RESULT_STATE;
1228 for ( int i = 0; i < iov->m_Count; i++ )
1229 total_size += iov->m_iovec[i].iov_len;
1231 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1233 if ( write_size == -1L || write_size != total_size )
1235 DefaultLogSink().Error("writev failed (%d)", errno);
1236 return RESULT_WRITEFAIL;
1239 for (int i = 0; i < iov->m_Count; ++i)
1241 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1245 *bytes_written = write_size;
1251 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1253 KM_TEST_NULL_L(buf);
1256 if ( bytes_written == 0 )
1257 bytes_written = &tmp_int;
1259 if ( m_Handle == -1L )
1260 return RESULT_STATE;
1262 int write_size = write(m_Handle, buf, buf_len);
1263 MaybeHash(buf, buf_len);
1265 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1267 DefaultLogSink().Error("write failed (%d)", errno);
1268 return RESULT_WRITEFAIL;
1271 *bytes_written = write_size;
1278 //------------------------------------------------------------------------------------------
1283 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1286 ui32_t read_size = 0;
1290 Result_t result = File.OpenRead(filename);
1292 if ( KM_SUCCESS(result) )
1294 fsize = File.Size();
1296 if ( fsize > (Kumu::fpos_t)max_size )
1298 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1299 return RESULT_ALLOC;
1304 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1305 return RESULT_READFAIL;
1308 result = ReadBuf.Capacity((ui32_t)fsize);
1311 if ( KM_SUCCESS(result) )
1312 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1314 if ( KM_SUCCESS(result) )
1315 outString.assign((const char*)ReadBuf.RoData(), read_size);
1323 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1326 ui32_t write_count = 0;
1328 Result_t result = File.OpenWrite(filename);
1330 if ( KM_SUCCESS(result) )
1331 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1336 //------------------------------------------------------------------------------------------
1341 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1344 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1345 Result_t result = Buffer.Capacity(file_size);
1347 if ( KM_SUCCESS(result) )
1349 ui32_t read_count = 0;
1352 result = Reader.OpenRead(Filename);
1354 if ( KM_SUCCESS(result) )
1355 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1357 if ( KM_SUCCESS(result) )
1359 assert(file_size == read_count);
1360 Buffer.Length(read_count);
1361 MemIOReader MemReader(&Buffer);
1362 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1371 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1374 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1376 if ( KM_SUCCESS(result) )
1378 ui32_t write_count = 0;
1380 MemIOWriter MemWriter(&Buffer);
1382 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1384 if ( KM_SUCCESS(result) )
1386 Buffer.Length(MemWriter.Length());
1387 result = Writer.OpenWrite(Filename);
1391 DefaultLogSink().Error("Object.Archive() failed in WriteObjectIntoFile()");
1394 if ( KM_SUCCESS(result) )
1395 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1401 //------------------------------------------------------------------------------------------
1406 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1408 ui32_t file_size = FileSize(Filename);
1409 Result_t result = Buffer.Capacity(file_size);
1411 if ( KM_SUCCESS(result) )
1413 ui32_t read_count = 0;
1416 result = Reader.OpenRead(Filename);
1418 if ( KM_SUCCESS(result) )
1419 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1421 if ( KM_SUCCESS(result) )
1423 if ( file_size != read_count)
1424 return RESULT_READFAIL;
1426 Buffer.Length(read_count);
1435 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1437 ui32_t write_count = 0;
1440 Result_t result = Writer.OpenWrite(Filename);
1442 if ( KM_SUCCESS(result) )
1443 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1445 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1447 DefaultLogSink().Error("WriteBufferIntoFile failed (%d)", result.Value());
1448 return RESULT_WRITEFAIL;
1454 //------------------------------------------------------------------------------------------
1457 Kumu::DirScanner::DirScanner()
1458 : _iterator(boost::filesystem::directory_iterator())
1464 Kumu::DirScanner::Open(const std::string& filename)
1466 if (!boost::filesystem::is_directory(filename))
1467 return RESULT_NOT_FOUND;
1469 _iterator = boost::filesystem::directory_iterator(filename);
1476 Kumu::DirScanner::GetNext(char* filename)
1478 if (_iterator == boost::filesystem::directory_iterator())
1479 return RESULT_ENDOFFILE;
1481 #if BOOST_FILESYSTEM_VERSION == 3
1482 std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1484 std::string f = boost::filesystem::path(*_iterator).filename();
1486 strncpy(filename, f.c_str(), MaxFilePath);
1491 //------------------------------------------------------------------------------------------
1494 // Attention Windows users: make sure to use the proper separator character
1495 // with these functions.
1498 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1501 Kumu::CreateDirectoriesInPath(const std::string& Path)
1503 bool abs = PathIsAbsolute(Path);
1504 PathCompList_t PathComps, TmpPathComps;
1506 PathToComponents(Path, PathComps);
1508 while ( ! PathComps.empty() )
1510 TmpPathComps.push_back(PathComps.front());
1511 PathComps.pop_front();
1512 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1514 if ( ! PathIsDirectory(tmp_path) )
1517 if ( _mkdir(tmp_path.c_str()) != 0 )
1519 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1522 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1523 tmp_path.c_str(), strerror(errno));
1524 return RESULT_DIR_CREATE;
1535 Kumu::DeleteFile(const std::string& filename)
1537 if ( _unlink(filename.c_str()) == 0 )
1543 case ENOTDIR: return RESULT_NOTAFILE;
1548 case EPERM: return RESULT_NO_PERM;
1551 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1557 h__DeletePath(const std::string& pathname)
1559 if ( pathname.empty() )
1560 return RESULT_NULL_STR;
1562 Result_t result = RESULT_OK;
1564 if ( ! PathIsDirectory(pathname) )
1566 result = DeleteFile(pathname);
1572 char next_file[Kumu::MaxFilePath];
1573 result = TestDir.Open(pathname.c_str());
1575 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1577 if ( next_file[0] == '.' )
1579 if ( next_file[1] == 0 )
1580 continue; // don't delete 'this'
1582 if ( next_file[1] == '.' && next_file[2] == 0 )
1583 continue; // don't delete 'this' parent
1586 result = h__DeletePath(pathname + std::string("/") + next_file);
1590 if ( _rmdir(pathname.c_str()) != 0 )
1596 result = RESULT_NOTAFILE;
1603 result = RESULT_NO_PERM;
1607 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1608 result = RESULT_FAIL;
1618 Kumu::DeletePath(const std::string& pathname)
1620 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1621 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1622 return h__DeletePath(c_pathname);
1628 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1630 DirScanner source_dir;
1631 char next_file[Kumu::MaxFilePath];
1633 Result_t result = source_dir.Open(path);
1635 if ( KM_FAILURE(result) )
1638 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1640 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1641 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1644 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1647 return DeletePath(path);
1651 //------------------------------------------------------------------------------------------
1656 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1659 ULARGE_INTEGER lTotalNumberOfBytes;
1660 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1662 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1665 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1666 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1670 HRESULT last_error = ::GetLastError();
1672 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1677 if ( statfs(path.c_str(), &s) == 0 )
1679 if ( s.f_blocks < 1 )
1681 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1682 path.c_str(), s.f_blocks);
1686 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1687 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1694 case ENOTDIR: return RESULT_NOTAFILE;
1695 case EACCES: return RESULT_NO_PERM;
1698 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1705 // end KM_fileio.cpp