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
28 \version $Id: KM_fileio.cpp,v 1.40 2015/10/07 16:58:03 jhurst Exp $
29 \brief portable file i/o
32 #include <KM_fileio.h>
43 #define _getcwd getcwd
44 #define _unlink unlink
48 // only needed by GetExecutablePath()
49 #if defined(KM_MACOSX)
50 #include <mach-o/dyld.h>
56 typedef struct _stati64 fstat_t;
60 // win32 has WriteFileGather() and ReadFileScatter() but they
61 // demand page alignment and page sizing, making them unsuitable
62 // for use with arbitrary buffer sizes.
64 char* iov_base; // stupid iovec uses char*
68 # if defined(__linux__)
69 # include <sys/statfs.h>
71 # include <sys/param.h>
72 # include <sys/mount.h>
77 typedef struct stat fstat_t;
83 do_stat(const char* path, fstat_t* stat_info)
85 KM_TEST_NULL_STR_L(path);
86 KM_TEST_NULL_L(stat_info);
88 Kumu::Result_t result = Kumu::RESULT_OK;
91 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
93 int const wn = MultiByteToWideChar (CP_UTF8, 0, path, -1, 0, 0);
94 wchar_t* buffer = new wchar_t[wn];
95 if (MultiByteToWideChar (CP_UTF8, 0, path, -1, buffer, wn) == 0)
98 return Kumu::RESULT_FAIL;
101 if ( _wstati64(buffer, stat_info) == (__int64)-1 )
102 result = Kumu::RESULT_FILEOPEN;
106 ::SetErrorMode( prev );
108 if ( stat(path, stat_info) == -1L )
109 result = Kumu::RESULT_FILEOPEN;
111 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
112 result = Kumu::RESULT_FILEOPEN;
121 static Kumu::Result_t
122 do_fstat(FileHandle handle, fstat_t* stat_info)
124 KM_TEST_NULL_L(stat_info);
126 Kumu::Result_t result = Kumu::RESULT_OK;
128 if ( fstat(handle, stat_info) == -1L )
129 result = Kumu::RESULT_FILEOPEN;
131 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
132 result = Kumu::RESULT_FILEOPEN;
142 Kumu::PathExists(const std::string& pathname)
144 if ( pathname.empty() )
149 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
157 Kumu::PathIsFile(const std::string& pathname)
159 if ( pathname.empty() )
164 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
166 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
176 Kumu::PathIsDirectory(const std::string& pathname)
178 if ( pathname.empty() )
183 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
185 if ( info.st_mode & S_IFDIR )
194 Kumu::FileSize(const std::string& pathname)
196 if ( pathname.empty() )
201 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
203 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
204 return(info.st_size);
212 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
214 PathCompList_t::const_iterator i;
215 for ( i = in_list.begin(); i != in_list.end(); ++i )
219 if ( ! out_list.empty() )
224 else if ( *i != "." )
226 out_list.push_back(*i);
233 Kumu::PathMakeCanonical(const std::string& Path, char separator)
235 PathCompList_t in_list, out_list;
236 bool is_absolute = PathIsAbsolute(Path, separator);
237 PathToComponents(Path, in_list, separator);
238 make_canonical_list(in_list, out_list);
241 return ComponentsToAbsolutePath(out_list, separator);
243 return ComponentsToPath(out_list, separator);
248 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
250 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
254 Kumu::PathCompList_t&
255 Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
259 PathCompList_t tmp_list = km_token_split(path, std::string(s));
260 PathCompList_t::const_iterator i;
262 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
266 component_list.push_back(*i);
270 return component_list;
275 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
280 PathCompList_t::const_iterator ci = CList.begin();
281 std::string out_path = *ci;
283 for ( ci++; ci != CList.end(); ci++ )
284 out_path += separator + *ci;
291 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
293 std::string out_path;
296 out_path = separator;
299 PathCompList_t::const_iterator ci;
301 for ( ci = CList.begin(); ci != CList.end(); ci++ )
302 out_path += separator + *ci;
310 Kumu::PathHasComponents(const std::string& Path, char separator)
312 if ( strchr(Path.c_str(), separator) == 0 )
320 Kumu::PathIsAbsolute(const std::string& Path, char separator)
325 if ( Path[0] == separator)
335 char cwd_buf [MaxFilePath];
336 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
338 DefaultLogSink().Error("Error retrieving current working directory.");
347 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
356 if ( PathIsAbsolute(Path, separator) )
357 return PathMakeCanonical(Path);
359 PathCompList_t in_list, out_list;
360 PathToComponents(PathJoin(PathCwd(), Path), in_list);
361 make_canonical_list(in_list, out_list);
363 return ComponentsToAbsolutePath(out_list);
368 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
370 size_t pos = Path.find(Parent);
372 if ( pos == 0 ) // Parent found at offset 0
373 return Path.substr(Parent.size()+1);
380 Kumu::PathBasename(const std::string& Path, char separator)
382 PathCompList_t CList;
383 PathToComponents(Path, CList, separator);
393 Kumu::PathDirname(const std::string& Path, char separator)
395 PathCompList_t CList;
396 bool is_absolute = PathIsAbsolute(Path, separator);
397 PathToComponents(Path, CList, separator);
400 return is_absolute ? "/" : "";
405 return ComponentsToAbsolutePath(CList, separator);
407 return ComponentsToPath(CList, separator);
412 Kumu::PathGetExtension(const std::string& Path)
414 std::string Basename = PathBasename(Path);
415 const char* p = strrchr(Basename.c_str(), '.');
425 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
427 std::string Basename = PathBasename(Path);
428 const char* p = strrchr(Basename.c_str(), '.');
431 Basename = Basename.substr(0, p - Basename.c_str());
433 if ( Extension.empty() )
436 return Basename + "." + Extension;
441 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
443 return Path1 + separator + Path2;
448 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
450 return Path1 + separator + Path2 + separator + Path3;
455 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
456 const std::string& Path3, const std::string& Path4, char separator)
458 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
462 // returns false if link cannot be read
465 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
467 PathCompList_t in_list, out_list;
468 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
469 PathCompList_t::iterator i;
470 char link_buf[MaxFilePath];
472 for ( i = in_list.begin(); i != in_list.end(); ++i )
474 assert ( *i != ".." && *i != "." );
475 out_list.push_back(*i);
479 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
480 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
482 if ( link_size == -1 )
484 if ( errno == EINVAL )
487 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
491 assert(link_size < MaxFilePath);
492 link_buf[link_size] = 0;
493 std::string tmp_path;
496 if ( PathIsAbsolute(link_buf) )
502 tmp_path = PathJoin(PathDirname(next_link), link_buf);
505 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
509 resolved_path = ComponentsToAbsolutePath(out_list, separator);
514 // TODO: is there a reasonable equivalent to be written for win32?
517 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
519 resolved_path = link_path;
526 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
527 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
529 PathList_t::const_iterator si;
530 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
532 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
534 if ( one_shot && ! FoundPaths.empty() )
543 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
544 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
546 char name_buf[MaxFilePath];
549 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
551 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
553 if ( name_buf[0] == '.' ) continue; // no hidden files
554 std::string tmp_path = SearchDir + separator + name_buf;
556 if ( PathIsDirectory(tmp_path.c_str()) )
557 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
559 else if ( Pattern.Match(name_buf) )
561 FoundPaths.push_back(SearchDir + separator + name_buf);
575 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
577 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
582 regerror(result, &m_regex, buf, 128);
583 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
588 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
589 m_regex = rhs.m_regex;
592 Kumu::PathMatchRegex::~PathMatchRegex() {
597 Kumu::PathMatchRegex::Match(const std::string& s) const {
598 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
604 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
606 std::string regex; // convert glob to regex
608 for ( const char* p = glob.c_str(); *p != 0; p++ )
612 case '.': regex += "\\."; break;
613 case '*': regex += ".*"; break;
614 case '?': regex += ".?"; break;
615 default: regex += *p;
620 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
625 regerror(result, &m_regex, buf, 128);
626 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
631 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
632 m_regex = rhs.m_regex;
635 Kumu::PathMatchGlob::~PathMatchGlob() {
640 Kumu::PathMatchGlob::Match(const std::string& s) const {
641 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
647 //------------------------------------------------------------------------------------------
649 #define X_BUFSIZE 1024
653 Kumu::GetExecutablePath(const std::string& default_path)
655 char path[X_BUFSIZE] = {0};
656 bool success = false;
658 #if defined(KM_WIN32)
659 DWORD size = X_BUFSIZE;
660 DWORD rc = GetModuleFileName(0, path, size);
661 success = ( rc != 0 );
662 #elif defined(KM_MACOSX)
663 uint32_t size = X_BUFSIZE;
664 int rc = _NSGetExecutablePath(path, &size);
665 success = ( rc != -1 );
666 #elif defined(__linux__)
667 size_t size = X_BUFSIZE;
668 ssize_t rc = readlink("/proc/self/exe", path, size);
669 success = ( rc != -1 );
670 #elif defined(__OpenBSD__) || defined(__FreeBSD__)
671 size_t size = X_BUFSIZE;
672 ssize_t rc = readlink("/proc/curproc/file", path, size);
673 success = ( rc != -1 );
674 #elif defined(__FreeBSD__)
675 size_t size = X_BUFSIZE;
676 ssize_t rc = readlink("/proc/curproc/file", path, size);
677 success = ( rc != -1 );
678 #elif defined(__NetBSD__)
679 size_t size = X_BUFSIZE;
680 ssize_t rc = readlink("/proc/curproc/file", path, size);
681 success = ( rc != -1 );
683 #error GetExecutablePath --> Create a method for obtaining the executable name
688 return Kumu::PathMakeCanonical(path);
695 //------------------------------------------------------------------------------------------
696 // portable aspects of the file classes
698 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
701 class Kumu::FileWriter::h__iovec
705 struct iovec m_iovec[IOVecMaxEntries];
706 h__iovec() : m_Count(0) {}
713 Kumu::FileReader::Size() const
716 return FileSize(m_Filename.c_str());
720 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
722 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
723 return(info.st_size);
730 // these are declared here instead of in the header file
731 // because we have a mem_ptr that is managing a hidden class
732 Kumu::FileWriter::FileWriter()
735 Kumu::FileWriter::~FileWriter() {}
739 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
741 assert( ! m_IOVec.empty() );
742 register h__iovec* iov = m_IOVec;
745 if ( iov->m_Count >= IOVecMaxEntries )
747 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
749 return RESULT_WRITEFAIL;
752 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
753 iov->m_iovec[iov->m_Count].iov_len = buf_len;
760 Kumu::FileWriter::StartHashing()
763 MD5_Init (&m_MD5Context);
767 Kumu::FileWriter::MaybeHash(void const * data, int size)
771 MD5_Update (&m_MD5Context, data, size);
776 Kumu::FileWriter::StopHashing()
780 unsigned char digest[MD5_DIGEST_LENGTH];
781 MD5_Final (digest, &m_MD5Context);
784 for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
786 s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
796 //------------------------------------------------------------------------------------------
800 Kumu::FileReader::OpenRead(const std::string& filename) const
802 const_cast<FileReader*>(this)->m_Filename = filename;
804 // suppress popup window on error
805 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
807 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename, -1, 0, 0);
808 wchar_t* buffer = new wchar_t[wn];
809 if (MultiByteToWideChar (CP_UTF8, 0, filename, -1, buffer, wn) == 0)
812 return Kumu::RESULT_FAIL;
814 const_cast<FileReader*>(this)->m_Handle = ::CreateFileW(buffer,
815 (GENERIC_READ), // open for reading
816 FILE_SHARE_READ, // share for reading
818 OPEN_EXISTING, // read
819 FILE_ATTRIBUTE_NORMAL, // normal file
820 NULL // no template file
825 ::SetErrorMode(prev);
827 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
828 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
833 Kumu::FileReader::Close() const
835 if ( m_Handle == INVALID_HANDLE_VALUE )
836 return Kumu::RESULT_FILEOPEN;
838 // suppress popup window on error
839 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
840 BOOL result = ::CloseHandle(m_Handle);
841 ::SetErrorMode(prev);
842 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
844 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
849 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
851 if ( m_Handle == INVALID_HANDLE_VALUE )
852 return Kumu::RESULT_STATE;
855 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
856 in.QuadPart = position;
857 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
858 HRESULT LastError = GetLastError();
859 ::SetErrorMode(prev);
861 if ( (LastError != NO_ERROR
862 && (in.LowPart == INVALID_SET_FILE_POINTER
863 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
864 return Kumu::RESULT_READFAIL;
866 return Kumu::RESULT_OK;
871 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
875 if ( m_Handle == INVALID_HANDLE_VALUE )
876 return Kumu::RESULT_FILEOPEN;
879 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
880 in.QuadPart = (__int64)0;
881 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
882 HRESULT LastError = GetLastError();
883 ::SetErrorMode(prev);
885 if ( (LastError != NO_ERROR
886 && (in.LowPart == INVALID_SET_FILE_POINTER
887 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
888 return Kumu::RESULT_READFAIL;
890 *pos = (Kumu::fpos_t)in.QuadPart;
891 return Kumu::RESULT_OK;
896 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
899 Result_t result = Kumu::RESULT_OK;
903 if ( read_count == 0 )
904 read_count = &tmp_int;
908 if ( m_Handle == INVALID_HANDLE_VALUE )
909 return Kumu::RESULT_FILEOPEN;
911 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
912 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
913 result = Kumu::RESULT_READFAIL;
915 ::SetErrorMode(prev);
917 if ( tmp_count == 0 ) /* EOF */
918 result = Kumu::RESULT_ENDOFFILE;
920 if ( KM_SUCCESS(result) )
921 *read_count = tmp_count;
928 //------------------------------------------------------------------------------------------
933 Kumu::FileWriter::OpenWrite(const std::string& filename)
935 m_Filename = filename;
937 // suppress popup window on error
938 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
940 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename, -1, 0, 0);
941 wchar_t* buffer = new wchar_t[wn];
942 if (MultiByteToWideChar (CP_UTF8, 0, filename, -1, buffer, wn) == 0)
945 return Kumu::RESULT_FAIL;
948 m_Handle = ::CreateFileW(buffer,
949 (GENERIC_WRITE|GENERIC_READ), // open for reading
950 FILE_SHARE_READ, // share for reading
952 CREATE_ALWAYS, // overwrite (beware!)
953 FILE_ATTRIBUTE_NORMAL, // normal file
954 NULL // no template file
959 ::SetErrorMode(prev);
961 if ( m_Handle == INVALID_HANDLE_VALUE )
962 return Kumu::RESULT_FILEOPEN;
964 m_IOVec = new h__iovec;
965 return Kumu::RESULT_OK;
968 /** @param filename File name (UTF-8 encoded) */
970 Kumu::FileWriter::OpenModify(const char* filename)
972 KM_TEST_NULL_STR_L(filename);
973 m_Filename = filename;
975 // suppress popup window on error
976 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
978 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename, -1, 0, 0);
979 wchar_t* buffer = new wchar_t[wn];
980 if (MultiByteToWideChar (CP_UTF8, 0, filename, -1, buffer, wn) == 0) {
982 return Kumu::RESULT_FAIL;
985 m_Handle = ::CreateFileW(buffer,
986 (GENERIC_WRITE|GENERIC_READ), // open for reading
987 FILE_SHARE_READ, // share for reading
989 OPEN_ALWAYS, // don't truncate existing
990 FILE_ATTRIBUTE_NORMAL, // normal file
991 NULL // no template file
996 ::SetErrorMode(prev);
998 if ( m_Handle == INVALID_HANDLE_VALUE )
999 return Kumu::RESULT_FILEOPEN;
1001 m_IOVec = new h__iovec;
1002 return Kumu::RESULT_OK;
1008 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1010 assert( ! m_IOVec.empty() );
1011 register h__iovec* iov = m_IOVec;
1014 if ( bytes_written == 0 )
1015 bytes_written = &tmp_int;
1017 if ( m_Handle == INVALID_HANDLE_VALUE )
1018 return Kumu::RESULT_STATE;
1021 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1022 Result_t result = Kumu::RESULT_OK;
1024 // AFAIK, there is no writev() equivalent in the win32 API
1025 for ( register int i = 0; i < iov->m_Count; i++ )
1027 ui32_t tmp_count = 0;
1028 BOOL wr_result = ::WriteFile(m_Handle,
1029 iov->m_iovec[i].iov_base,
1030 iov->m_iovec[i].iov_len,
1034 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1036 result = Kumu::RESULT_WRITEFAIL;
1040 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1041 *bytes_written += tmp_count;
1044 ::SetErrorMode(prev);
1045 iov->m_Count = 0; // error nor not, all is lost
1052 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1054 KM_TEST_NULL_L(buf);
1057 if ( bytes_written == 0 )
1058 bytes_written = &tmp_int;
1060 if ( m_Handle == INVALID_HANDLE_VALUE )
1061 return Kumu::RESULT_STATE;
1063 // suppress popup window on error
1064 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1065 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1066 ::SetErrorMode(prev);
1068 if ( result == 0 || *bytes_written != buf_len )
1069 return Kumu::RESULT_WRITEFAIL;
1071 MaybeHash(buf, buf_len);
1073 return Kumu::RESULT_OK;
1077 //------------------------------------------------------------------------------------------
1082 Kumu::FileReader::OpenRead(const std::string& filename) const
1084 const_cast<FileReader*>(this)->m_Filename = filename;
1085 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1086 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1091 Kumu::FileReader::Close() const
1093 if ( m_Handle == -1L )
1094 return RESULT_FILEOPEN;
1097 const_cast<FileReader*>(this)->m_Handle = -1L;
1103 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1105 if ( m_Handle == -1L )
1106 return RESULT_FILEOPEN;
1108 if ( lseek(m_Handle, position, whence) == -1L )
1109 return RESULT_BADSEEK;
1116 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1118 KM_TEST_NULL_L(pos);
1120 if ( m_Handle == -1L )
1121 return RESULT_FILEOPEN;
1123 Kumu::fpos_t tmp_pos;
1125 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1126 return RESULT_READFAIL;
1134 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1136 KM_TEST_NULL_L(buf);
1137 i32_t tmp_count = 0;
1140 if ( read_count == 0 )
1141 read_count = &tmp_int;
1145 if ( m_Handle == -1L )
1146 return RESULT_FILEOPEN;
1148 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1149 return RESULT_READFAIL;
1151 *read_count = tmp_count;
1152 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1156 //------------------------------------------------------------------------------------------
1161 Kumu::FileWriter::OpenWrite(const std::string& filename)
1163 m_Filename = filename;
1164 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
1166 if ( m_Handle == -1L )
1168 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1169 return RESULT_FILEOPEN;
1172 m_IOVec = new h__iovec;
1178 Kumu::FileWriter::OpenModify(const std::string& filename)
1180 m_Filename = filename;
1181 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1183 if ( m_Handle == -1L )
1185 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1186 return RESULT_FILEOPEN;
1189 m_IOVec = new h__iovec;
1195 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1197 assert( ! m_IOVec.empty() );
1198 register h__iovec* iov = m_IOVec;
1201 if ( bytes_written == 0 )
1202 bytes_written = &tmp_int;
1204 if ( m_Handle == -1L )
1205 return RESULT_STATE;
1208 for ( int i = 0; i < iov->m_Count; i++ )
1209 total_size += iov->m_iovec[i].iov_len;
1211 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1213 if ( write_size == -1L || write_size != total_size )
1214 return RESULT_WRITEFAIL;
1216 for (int i = 0; i < iov->m_Count; ++i)
1218 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1222 *bytes_written = write_size;
1228 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1230 KM_TEST_NULL_L(buf);
1233 if ( bytes_written == 0 )
1234 bytes_written = &tmp_int;
1236 if ( m_Handle == -1L )
1237 return RESULT_STATE;
1239 int write_size = write(m_Handle, buf, buf_len);
1240 MaybeHash(buf, buf_len);
1242 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1243 return RESULT_WRITEFAIL;
1245 *bytes_written = write_size;
1252 //------------------------------------------------------------------------------------------
1257 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1260 ui32_t read_size = 0;
1264 Result_t result = File.OpenRead(filename);
1266 if ( KM_SUCCESS(result) )
1268 fsize = File.Size();
1270 if ( fsize > (Kumu::fpos_t)max_size )
1272 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1273 return RESULT_ALLOC;
1278 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1279 return RESULT_READFAIL;
1282 result = ReadBuf.Capacity((ui32_t)fsize);
1285 if ( KM_SUCCESS(result) )
1286 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1288 if ( KM_SUCCESS(result) )
1289 outString.assign((const char*)ReadBuf.RoData(), read_size);
1297 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1300 ui32_t write_count = 0;
1302 Result_t result = File.OpenWrite(filename);
1304 if ( KM_SUCCESS(result) )
1305 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1310 //------------------------------------------------------------------------------------------
1315 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1318 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1319 Result_t result = Buffer.Capacity(file_size);
1321 if ( KM_SUCCESS(result) )
1323 ui32_t read_count = 0;
1326 result = Reader.OpenRead(Filename);
1328 if ( KM_SUCCESS(result) )
1329 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1331 if ( KM_SUCCESS(result) )
1333 assert(file_size == read_count);
1334 Buffer.Length(read_count);
1335 MemIOReader MemReader(&Buffer);
1336 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1345 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1348 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1350 if ( KM_SUCCESS(result) )
1352 ui32_t write_count = 0;
1354 MemIOWriter MemWriter(&Buffer);
1356 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1358 if ( KM_SUCCESS(result) )
1360 Buffer.Length(MemWriter.Length());
1361 result = Writer.OpenWrite(Filename);
1364 if ( KM_SUCCESS(result) )
1365 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1371 //------------------------------------------------------------------------------------------
1376 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1378 ui32_t file_size = FileSize(Filename);
1379 Result_t result = Buffer.Capacity(file_size);
1381 if ( KM_SUCCESS(result) )
1383 ui32_t read_count = 0;
1386 result = Reader.OpenRead(Filename);
1388 if ( KM_SUCCESS(result) )
1389 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1391 if ( KM_SUCCESS(result) )
1393 if ( file_size != read_count)
1394 return RESULT_READFAIL;
1396 Buffer.Length(read_count);
1405 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1407 ui32_t write_count = 0;
1410 Result_t result = Writer.OpenWrite(Filename);
1412 if ( KM_SUCCESS(result) )
1413 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1415 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1416 return RESULT_WRITEFAIL;
1421 //------------------------------------------------------------------------------------------
1424 Kumu::DirScanner::DirScanner()
1425 : _iterator(boost::filesystem::directory_iterator())
1431 Kumu::DirScanner::Open(const std::string& filename)
1433 if (!boost::filesystem::is_directory(filename))
1434 return RESULT_NOT_FOUND;
1436 _iterator = boost::filesystem::directory_iterator(filename);
1443 Kumu::DirScanner::GetNext(char* filename)
1445 if (_iterator == boost::filesystem::directory_iterator())
1446 return RESULT_ENDOFFILE;
1448 #if BOOST_FILESYSTEM_VERSION == 3
1449 std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1451 std::string f = boost::filesystem::path(*_iterator).filename();
1453 strncpy(filename, f.c_str(), MaxFilePath);
1458 //------------------------------------------------------------------------------------------
1461 // Attention Windows users: make sure to use the proper separator character
1462 // with these functions.
1465 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1468 Kumu::CreateDirectoriesInPath(const std::string& Path)
1470 bool abs = PathIsAbsolute(Path);
1471 PathCompList_t PathComps, TmpPathComps;
1473 PathToComponents(Path, PathComps);
1475 while ( ! PathComps.empty() )
1477 TmpPathComps.push_back(PathComps.front());
1478 PathComps.pop_front();
1479 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1481 if ( ! PathIsDirectory(tmp_path) )
1484 if ( _mkdir(tmp_path.c_str()) != 0 )
1486 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1489 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1490 tmp_path.c_str(), strerror(errno));
1491 return RESULT_DIR_CREATE;
1502 Kumu::DeleteFile(const std::string& filename)
1504 if ( _unlink(filename.c_str()) == 0 )
1510 case ENOTDIR: return RESULT_NOTAFILE;
1515 case EPERM: return RESULT_NO_PERM;
1518 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1524 h__DeletePath(const std::string& pathname)
1526 if ( pathname.empty() )
1527 return RESULT_NULL_STR;
1529 Result_t result = RESULT_OK;
1531 if ( ! PathIsDirectory(pathname) )
1533 result = DeleteFile(pathname);
1539 char next_file[Kumu::MaxFilePath];
1540 result = TestDir.Open(pathname.c_str());
1542 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1544 if ( next_file[0] == '.' )
1546 if ( next_file[1] == 0 )
1547 continue; // don't delete 'this'
1549 if ( next_file[1] == '.' && next_file[2] == 0 )
1550 continue; // don't delete 'this' parent
1553 result = h__DeletePath(pathname + std::string("/") + next_file);
1557 if ( _rmdir(pathname.c_str()) != 0 )
1563 result = RESULT_NOTAFILE;
1570 result = RESULT_NO_PERM;
1574 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1575 result = RESULT_FAIL;
1585 Kumu::DeletePath(const std::string& pathname)
1587 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1588 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1589 return h__DeletePath(c_pathname);
1595 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1597 DirScanner source_dir;
1598 char next_file[Kumu::MaxFilePath];
1600 Result_t result = source_dir.Open(path);
1602 if ( KM_FAILURE(result) )
1605 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1607 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1608 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1611 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1614 return DeletePath(path);
1618 //------------------------------------------------------------------------------------------
1623 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1626 ULARGE_INTEGER lTotalNumberOfBytes;
1627 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1629 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1632 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1633 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1637 HRESULT last_error = ::GetLastError();
1639 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1644 if ( statfs(path.c_str(), &s) == 0 )
1646 if ( s.f_blocks < 1 )
1648 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1649 path.c_str(), s.f_blocks);
1653 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1654 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1661 case ENOTDIR: return RESULT_NOTAFILE;
1662 case EACCES: return RESULT_NO_PERM;
1665 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1672 // end KM_fileio.cpp