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.c_str(), -1, 0, 0);
808 wchar_t* buffer = new wchar_t[wn];
809 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -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.c_str(), -1, 0, 0);
941 wchar_t* buffer = new wchar_t[wn];
942 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -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 std::string& filename)
972 m_Filename = filename;
974 // suppress popup window on error
975 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
977 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
978 wchar_t* buffer = new wchar_t[wn];
979 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0) {
981 return Kumu::RESULT_FAIL;
984 m_Handle = ::CreateFileW(buffer,
985 (GENERIC_WRITE|GENERIC_READ), // open for reading
986 FILE_SHARE_READ, // share for reading
988 OPEN_ALWAYS, // don't truncate existing
989 FILE_ATTRIBUTE_NORMAL, // normal file
990 NULL // no template file
995 ::SetErrorMode(prev);
997 if ( m_Handle == INVALID_HANDLE_VALUE )
998 return Kumu::RESULT_FILEOPEN;
1000 m_IOVec = new h__iovec;
1001 return Kumu::RESULT_OK;
1007 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1009 assert( ! m_IOVec.empty() );
1010 register h__iovec* iov = m_IOVec;
1013 if ( bytes_written == 0 )
1014 bytes_written = &tmp_int;
1016 if ( m_Handle == INVALID_HANDLE_VALUE )
1017 return Kumu::RESULT_STATE;
1020 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1021 Result_t result = Kumu::RESULT_OK;
1023 // AFAIK, there is no writev() equivalent in the win32 API
1024 for ( register int i = 0; i < iov->m_Count; i++ )
1026 ui32_t tmp_count = 0;
1027 BOOL wr_result = ::WriteFile(m_Handle,
1028 iov->m_iovec[i].iov_base,
1029 iov->m_iovec[i].iov_len,
1033 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1035 result = Kumu::RESULT_WRITEFAIL;
1039 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1040 *bytes_written += tmp_count;
1043 ::SetErrorMode(prev);
1044 iov->m_Count = 0; // error nor not, all is lost
1051 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1053 KM_TEST_NULL_L(buf);
1056 if ( bytes_written == 0 )
1057 bytes_written = &tmp_int;
1059 if ( m_Handle == INVALID_HANDLE_VALUE )
1060 return Kumu::RESULT_STATE;
1062 // suppress popup window on error
1063 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1064 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1065 ::SetErrorMode(prev);
1067 if ( result == 0 || *bytes_written != buf_len )
1068 return Kumu::RESULT_WRITEFAIL;
1070 MaybeHash(buf, buf_len);
1072 return Kumu::RESULT_OK;
1076 //------------------------------------------------------------------------------------------
1081 Kumu::FileReader::OpenRead(const std::string& filename) const
1083 const_cast<FileReader*>(this)->m_Filename = filename;
1084 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1085 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1090 Kumu::FileReader::Close() const
1092 if ( m_Handle == -1L )
1093 return RESULT_FILEOPEN;
1096 const_cast<FileReader*>(this)->m_Handle = -1L;
1102 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1104 if ( m_Handle == -1L )
1105 return RESULT_FILEOPEN;
1107 if ( lseek(m_Handle, position, whence) == -1L )
1108 return RESULT_BADSEEK;
1115 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1117 KM_TEST_NULL_L(pos);
1119 if ( m_Handle == -1L )
1120 return RESULT_FILEOPEN;
1122 Kumu::fpos_t tmp_pos;
1124 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1125 return RESULT_READFAIL;
1133 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1135 KM_TEST_NULL_L(buf);
1136 i32_t tmp_count = 0;
1139 if ( read_count == 0 )
1140 read_count = &tmp_int;
1144 if ( m_Handle == -1L )
1145 return RESULT_FILEOPEN;
1147 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1148 return RESULT_READFAIL;
1150 *read_count = tmp_count;
1151 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1155 //------------------------------------------------------------------------------------------
1160 Kumu::FileWriter::OpenWrite(const std::string& filename)
1162 m_Filename = filename;
1163 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
1165 if ( m_Handle == -1L )
1167 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1168 return RESULT_FILEOPEN;
1171 m_IOVec = new h__iovec;
1177 Kumu::FileWriter::OpenModify(const std::string& filename)
1179 m_Filename = filename;
1180 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1182 if ( m_Handle == -1L )
1184 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1185 return RESULT_FILEOPEN;
1188 m_IOVec = new h__iovec;
1194 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1196 assert( ! m_IOVec.empty() );
1197 register h__iovec* iov = m_IOVec;
1200 if ( bytes_written == 0 )
1201 bytes_written = &tmp_int;
1203 if ( m_Handle == -1L )
1204 return RESULT_STATE;
1207 for ( int i = 0; i < iov->m_Count; i++ )
1208 total_size += iov->m_iovec[i].iov_len;
1210 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1212 if ( write_size == -1L || write_size != total_size )
1213 return RESULT_WRITEFAIL;
1215 for (int i = 0; i < iov->m_Count; ++i)
1217 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1221 *bytes_written = write_size;
1227 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1229 KM_TEST_NULL_L(buf);
1232 if ( bytes_written == 0 )
1233 bytes_written = &tmp_int;
1235 if ( m_Handle == -1L )
1236 return RESULT_STATE;
1238 int write_size = write(m_Handle, buf, buf_len);
1239 MaybeHash(buf, buf_len);
1241 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1242 return RESULT_WRITEFAIL;
1244 *bytes_written = write_size;
1251 //------------------------------------------------------------------------------------------
1256 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1259 ui32_t read_size = 0;
1263 Result_t result = File.OpenRead(filename);
1265 if ( KM_SUCCESS(result) )
1267 fsize = File.Size();
1269 if ( fsize > (Kumu::fpos_t)max_size )
1271 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1272 return RESULT_ALLOC;
1277 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1278 return RESULT_READFAIL;
1281 result = ReadBuf.Capacity((ui32_t)fsize);
1284 if ( KM_SUCCESS(result) )
1285 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1287 if ( KM_SUCCESS(result) )
1288 outString.assign((const char*)ReadBuf.RoData(), read_size);
1296 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1299 ui32_t write_count = 0;
1301 Result_t result = File.OpenWrite(filename);
1303 if ( KM_SUCCESS(result) )
1304 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1309 //------------------------------------------------------------------------------------------
1314 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1317 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1318 Result_t result = Buffer.Capacity(file_size);
1320 if ( KM_SUCCESS(result) )
1322 ui32_t read_count = 0;
1325 result = Reader.OpenRead(Filename);
1327 if ( KM_SUCCESS(result) )
1328 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1330 if ( KM_SUCCESS(result) )
1332 assert(file_size == read_count);
1333 Buffer.Length(read_count);
1334 MemIOReader MemReader(&Buffer);
1335 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1344 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1347 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1349 if ( KM_SUCCESS(result) )
1351 ui32_t write_count = 0;
1353 MemIOWriter MemWriter(&Buffer);
1355 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1357 if ( KM_SUCCESS(result) )
1359 Buffer.Length(MemWriter.Length());
1360 result = Writer.OpenWrite(Filename);
1363 if ( KM_SUCCESS(result) )
1364 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1370 //------------------------------------------------------------------------------------------
1375 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1377 ui32_t file_size = FileSize(Filename);
1378 Result_t result = Buffer.Capacity(file_size);
1380 if ( KM_SUCCESS(result) )
1382 ui32_t read_count = 0;
1385 result = Reader.OpenRead(Filename);
1387 if ( KM_SUCCESS(result) )
1388 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1390 if ( KM_SUCCESS(result) )
1392 if ( file_size != read_count)
1393 return RESULT_READFAIL;
1395 Buffer.Length(read_count);
1404 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1406 ui32_t write_count = 0;
1409 Result_t result = Writer.OpenWrite(Filename);
1411 if ( KM_SUCCESS(result) )
1412 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1414 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1415 return RESULT_WRITEFAIL;
1420 //------------------------------------------------------------------------------------------
1423 Kumu::DirScanner::DirScanner()
1424 : _iterator(boost::filesystem::directory_iterator())
1430 Kumu::DirScanner::Open(const std::string& filename)
1432 if (!boost::filesystem::is_directory(filename))
1433 return RESULT_NOT_FOUND;
1435 _iterator = boost::filesystem::directory_iterator(filename);
1442 Kumu::DirScanner::GetNext(char* filename)
1444 if (_iterator == boost::filesystem::directory_iterator())
1445 return RESULT_ENDOFFILE;
1447 #if BOOST_FILESYSTEM_VERSION == 3
1448 std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1450 std::string f = boost::filesystem::path(*_iterator).filename();
1452 strncpy(filename, f.c_str(), MaxFilePath);
1457 //------------------------------------------------------------------------------------------
1460 // Attention Windows users: make sure to use the proper separator character
1461 // with these functions.
1464 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1467 Kumu::CreateDirectoriesInPath(const std::string& Path)
1469 bool abs = PathIsAbsolute(Path);
1470 PathCompList_t PathComps, TmpPathComps;
1472 PathToComponents(Path, PathComps);
1474 while ( ! PathComps.empty() )
1476 TmpPathComps.push_back(PathComps.front());
1477 PathComps.pop_front();
1478 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1480 if ( ! PathIsDirectory(tmp_path) )
1483 if ( _mkdir(tmp_path.c_str()) != 0 )
1485 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1488 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1489 tmp_path.c_str(), strerror(errno));
1490 return RESULT_DIR_CREATE;
1501 Kumu::DeleteFile(const std::string& filename)
1503 if ( _unlink(filename.c_str()) == 0 )
1509 case ENOTDIR: return RESULT_NOTAFILE;
1514 case EPERM: return RESULT_NO_PERM;
1517 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1523 h__DeletePath(const std::string& pathname)
1525 if ( pathname.empty() )
1526 return RESULT_NULL_STR;
1528 Result_t result = RESULT_OK;
1530 if ( ! PathIsDirectory(pathname) )
1532 result = DeleteFile(pathname);
1538 char next_file[Kumu::MaxFilePath];
1539 result = TestDir.Open(pathname.c_str());
1541 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1543 if ( next_file[0] == '.' )
1545 if ( next_file[1] == 0 )
1546 continue; // don't delete 'this'
1548 if ( next_file[1] == '.' && next_file[2] == 0 )
1549 continue; // don't delete 'this' parent
1552 result = h__DeletePath(pathname + std::string("/") + next_file);
1556 if ( _rmdir(pathname.c_str()) != 0 )
1562 result = RESULT_NOTAFILE;
1569 result = RESULT_NO_PERM;
1573 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1574 result = RESULT_FAIL;
1584 Kumu::DeletePath(const std::string& pathname)
1586 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1587 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1588 return h__DeletePath(c_pathname);
1594 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1596 DirScanner source_dir;
1597 char next_file[Kumu::MaxFilePath];
1599 Result_t result = source_dir.Open(path);
1601 if ( KM_FAILURE(result) )
1604 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1606 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1607 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1610 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1613 return DeletePath(path);
1617 //------------------------------------------------------------------------------------------
1622 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1625 ULARGE_INTEGER lTotalNumberOfBytes;
1626 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1628 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1631 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1632 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1636 HRESULT last_error = ::GetLastError();
1638 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1643 if ( statfs(path.c_str(), &s) == 0 )
1645 if ( s.f_blocks < 1 )
1647 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1648 path.c_str(), s.f_blocks);
1652 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1653 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1660 case ENOTDIR: return RESULT_NOTAFILE;
1661 case EACCES: return RESULT_NO_PERM;
1664 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1671 // end KM_fileio.cpp