2 Copyright (c) 2004-2011, 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.31 2011/03/08 19:03:47 jhurst Exp $
29 \brief portable file i/o
32 #include <KM_fileio.h>
43 #define _getcwd getcwd
44 #define _unlink unlink
51 typedef struct _stati64 fstat_t;
54 // win32 has WriteFileGather() and ReadFileScatter() but they
55 // demand page alignment and page sizing, making them unsuitable
56 // for use with arbitrary buffer sizes.
58 char* iov_base; // stupid iovec uses char*
62 # if defined(__linux__)
63 # include <sys/statfs.h>
65 # include <sys/param.h>
66 # include <sys/mount.h>
71 typedef struct stat fstat_t;
76 split(const std::string& str, char separator, std::list<std::string>& components)
78 const char* pstr = str.c_str();
79 const char* r = strchr(pstr, separator);
87 tmp_str.assign(pstr, (r - pstr));
88 components.push_back(tmp_str);
92 r = strchr(pstr, separator);
95 if( strlen(pstr) > 0 )
96 components.push_back(std::string(pstr));
101 static Kumu::Result_t
102 do_stat(const char* path, fstat_t* stat_info)
104 KM_TEST_NULL_STR_L(path);
105 KM_TEST_NULL_L(stat_info);
107 Kumu::Result_t result = Kumu::RESULT_OK;
110 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
112 int const wn = MultiByteToWideChar (CP_UTF8, 0, path, -1, 0, 0);
113 wchar_t* buffer = new wchar_t[wn];
114 if (MultiByteToWideChar (CP_UTF8, 0, path, -1, buffer, wn) == 0) {
116 return Kumu::RESULT_FAIL;
119 if ( _wstati64(buffer, stat_info) == (__int64)-1 )
120 result = Kumu::RESULT_FILEOPEN;
124 ::SetErrorMode( prev );
126 if ( stat(path, stat_info) == -1L )
127 result = Kumu::RESULT_FILEOPEN;
129 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
130 result = Kumu::RESULT_FILEOPEN;
139 static Kumu::Result_t
140 do_fstat(FileHandle handle, fstat_t* stat_info)
142 KM_TEST_NULL_L(stat_info);
144 Kumu::Result_t result = Kumu::RESULT_OK;
146 if ( fstat(handle, stat_info) == -1L )
147 result = Kumu::RESULT_FILEOPEN;
149 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
150 result = Kumu::RESULT_FILEOPEN;
160 Kumu::PathExists(const std::string& pathname)
162 if ( pathname.empty() )
167 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
175 Kumu::PathIsFile(const std::string& pathname)
177 if ( pathname.empty() )
182 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
184 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
194 Kumu::PathIsDirectory(const std::string& pathname)
196 if ( pathname.empty() )
201 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
203 if ( info.st_mode & S_IFDIR )
212 Kumu::FileSize(const std::string& pathname)
214 if ( pathname.empty() )
219 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
221 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
222 return(info.st_size);
229 static PathCompList_t&
230 s_PathMakeCanonical(PathCompList_t& CList, bool is_absolute)
232 PathCompList_t::iterator ci, ri; // component and removal iterators
234 for ( ci = CList.begin(); ci != CList.end(); ci++ )
236 if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
241 else if ( *ci == ".." && ci != CList.begin() )
260 Kumu::PathMakeCanonical(const std::string& Path, char separator)
262 PathCompList_t CList;
263 bool is_absolute = PathIsAbsolute(Path, separator);
264 s_PathMakeCanonical(PathToComponents(Path, CList, separator), is_absolute);
267 return ComponentsToAbsolutePath(CList, separator);
269 return ComponentsToPath(CList, separator);
274 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
276 return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
280 Kumu::PathCompList_t&
281 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
283 split(Path, separator, CList);
289 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
294 PathCompList_t::const_iterator ci = CList.begin();
295 std::string out_path = *ci;
297 for ( ci++; ci != CList.end(); ci++ )
298 out_path += separator + *ci;
305 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
307 std::string out_path;
310 out_path = separator;
313 PathCompList_t::const_iterator ci;
315 for ( ci = CList.begin(); ci != CList.end(); ci++ )
316 out_path += separator + *ci;
324 Kumu::PathHasComponents(const std::string& Path, char separator)
326 if ( strchr(Path.c_str(), separator) == 0 )
334 Kumu::PathIsAbsolute(const std::string& Path, char separator)
339 if ( Path[0] == separator)
347 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
351 std::string out_path;
352 out_path = separator;
356 if ( PathIsAbsolute(Path, separator) )
359 char cwd_buf [MaxFilePath];
360 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
362 DefaultLogSink().Error("Error retrieving current working directory.");
366 PathCompList_t CList;
367 PathToComponents(cwd_buf, CList);
368 CList.push_back(Path);
370 return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, true), separator);
375 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
377 size_t pos = Path.find(Parent);
379 if ( pos == 0 ) // Parent found at offset 0
380 return Path.substr(Parent.size()+1);
387 Kumu::PathBasename(const std::string& Path, char separator)
389 PathCompList_t CList;
390 PathToComponents(Path, CList, separator);
400 Kumu::PathDirname(const std::string& Path, char separator)
402 PathCompList_t CList;
403 bool is_absolute = PathIsAbsolute(Path, separator);
404 PathToComponents(Path, CList, separator);
407 return is_absolute ? "/" : "";
412 return ComponentsToAbsolutePath(CList, separator);
414 return ComponentsToPath(CList, separator);
419 Kumu::PathGetExtension(const std::string& Path)
421 std::string Basename = PathBasename(Path);
422 const char* p = strrchr(Basename.c_str(), '.');
432 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
434 std::string Basename = PathBasename(Path);
435 const char* p = strrchr(Basename.c_str(), '.');
438 Basename = Basename.substr(0, p - Basename.c_str());
440 if ( Extension.empty() )
443 return Basename + "." + Extension;
448 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
450 return Path1 + separator + Path2;
455 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
457 return Path1 + separator + Path2 + separator + Path3;
462 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
463 const std::string& Path3, const std::string& Path4, char separator)
465 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
470 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
471 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
473 PathList_t::const_iterator si;
474 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
476 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
478 if ( one_shot && ! FoundPaths.empty() )
487 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
488 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
490 char name_buf[MaxFilePath];
493 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
495 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
497 if ( name_buf[0] == '.' ) continue; // no hidden files
498 std::string tmp_path = SearchDir + separator + name_buf;
500 if ( PathIsDirectory(tmp_path.c_str()) )
501 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
503 else if ( Pattern.Match(name_buf) )
505 FoundPaths.push_back(SearchDir + separator + name_buf);
519 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
521 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
526 regerror(result, &m_regex, buf, 128);
527 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
532 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
533 m_regex = rhs.m_regex;
536 Kumu::PathMatchRegex::~PathMatchRegex() {
541 Kumu::PathMatchRegex::Match(const std::string& s) const {
542 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
548 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
550 std::string regex; // convert glob to regex
552 for ( const char* p = glob.c_str(); *p != 0; p++ )
556 case '.': regex += "\\."; break;
557 case '*': regex += ".*"; break;
558 case '?': regex += ".?"; break;
559 default: regex += *p;
564 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
569 regerror(result, &m_regex, buf, 128);
570 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
575 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
576 m_regex = rhs.m_regex;
579 Kumu::PathMatchGlob::~PathMatchGlob() {
584 Kumu::PathMatchGlob::Match(const std::string& s) const {
585 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
590 //------------------------------------------------------------------------------------------
591 // portable aspects of the file classes
593 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
596 class Kumu::FileWriter::h__iovec
600 struct iovec m_iovec[IOVecMaxEntries];
601 h__iovec() : m_Count(0) {}
608 Kumu::FileReader::Size() const
611 return FileSize(m_Filename.c_str());
615 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
617 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
618 return(info.st_size);
625 // these are declared here instead of in the header file
626 // because we have a mem_ptr that is managing a hidden class
627 Kumu::FileWriter::FileWriter()
631 Kumu::FileWriter::~FileWriter() {}
635 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
637 assert( ! m_IOVec.empty() );
638 register h__iovec* iov = m_IOVec;
641 if ( iov->m_Count >= IOVecMaxEntries )
643 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
645 return RESULT_WRITEFAIL;
648 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
649 iov->m_iovec[iov->m_Count].iov_len = buf_len;
656 Kumu::FileWriter::StartHashing()
659 MD5_Init (&m_MD5Context);
663 Kumu::FileWriter::MaybeHash(void const * data, int size)
666 MD5_Update (&m_MD5Context, data, size);
671 Kumu::FileWriter::StopHashing()
675 unsigned char digest[MD5_DIGEST_LENGTH];
676 MD5_Final (digest, &m_MD5Context);
679 for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
680 s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
688 //------------------------------------------------------------------------------------------
691 /** @param filename File name (UTF-8 encoded) */
693 Kumu::FileReader::OpenRead(const char* filename) const
695 KM_TEST_NULL_STR_L(filename);
696 const_cast<FileReader*>(this)->m_Filename = filename;
698 // suppress popup window on error
699 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
701 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename, -1, 0, 0);
702 wchar_t* buffer = new wchar_t[wn];
703 if (MultiByteToWideChar (CP_UTF8, 0, filename, -1, buffer, wn) == 0) {
705 return Kumu::RESULT_FAIL;
707 const_cast<FileReader*>(this)->m_Handle = ::CreateFileW(buffer,
708 (GENERIC_READ), // open for reading
709 FILE_SHARE_READ, // share for reading
711 OPEN_EXISTING, // read
712 FILE_ATTRIBUTE_NORMAL, // normal file
713 NULL // no template file
718 ::SetErrorMode(prev);
720 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
721 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
726 Kumu::FileReader::Close() const
728 if ( m_Handle == INVALID_HANDLE_VALUE )
729 return Kumu::RESULT_FILEOPEN;
731 // suppress popup window on error
732 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
733 BOOL result = ::CloseHandle(m_Handle);
734 ::SetErrorMode(prev);
735 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
737 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
742 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
744 if ( m_Handle == INVALID_HANDLE_VALUE )
745 return Kumu::RESULT_STATE;
748 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
749 in.QuadPart = position;
750 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
751 HRESULT LastError = GetLastError();
752 ::SetErrorMode(prev);
754 if ( (LastError != NO_ERROR
755 && (in.LowPart == INVALID_SET_FILE_POINTER
756 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
757 return Kumu::RESULT_READFAIL;
759 return Kumu::RESULT_OK;
764 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
768 if ( m_Handle == INVALID_HANDLE_VALUE )
769 return Kumu::RESULT_FILEOPEN;
772 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
773 in.QuadPart = (__int64)0;
774 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
775 HRESULT LastError = GetLastError();
776 ::SetErrorMode(prev);
778 if ( (LastError != NO_ERROR
779 && (in.LowPart == INVALID_SET_FILE_POINTER
780 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
781 return Kumu::RESULT_READFAIL;
783 *pos = (Kumu::fpos_t)in.QuadPart;
784 return Kumu::RESULT_OK;
789 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
792 Result_t result = Kumu::RESULT_OK;
796 if ( read_count == 0 )
797 read_count = &tmp_int;
801 if ( m_Handle == INVALID_HANDLE_VALUE )
802 return Kumu::RESULT_FILEOPEN;
804 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
805 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
806 result = Kumu::RESULT_READFAIL;
808 ::SetErrorMode(prev);
810 if ( tmp_count == 0 ) /* EOF */
811 result = Kumu::RESULT_ENDOFFILE;
813 if ( KM_SUCCESS(result) )
814 *read_count = tmp_count;
821 //------------------------------------------------------------------------------------------
824 /** @param filename File name (UTF-8 encoded) */
826 Kumu::FileWriter::OpenWrite(const char* filename)
828 KM_TEST_NULL_STR_L(filename);
829 m_Filename = filename;
831 // suppress popup window on error
832 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
834 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename, -1, 0, 0);
835 wchar_t* buffer = new wchar_t[wn];
836 if (MultiByteToWideChar (CP_UTF8, 0, filename, -1, buffer, wn) == 0) {
838 return Kumu::RESULT_FAIL;
841 m_Handle = ::CreateFileW(buffer,
842 (GENERIC_WRITE|GENERIC_READ), // open for reading
843 FILE_SHARE_READ, // share for reading
845 CREATE_ALWAYS, // overwrite (beware!)
846 FILE_ATTRIBUTE_NORMAL, // normal file
847 NULL // no template file
852 ::SetErrorMode(prev);
854 if ( m_Handle == INVALID_HANDLE_VALUE )
855 return Kumu::RESULT_FILEOPEN;
857 m_IOVec = new h__iovec;
858 return Kumu::RESULT_OK;
861 /** @param filename File name (UTF-8 encoded) */
863 Kumu::FileWriter::OpenModify(const char* filename)
865 KM_TEST_NULL_STR_L(filename);
866 m_Filename = filename;
868 // suppress popup window on error
869 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
871 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename, -1, 0, 0);
872 wchar_t* buffer = new wchar_t[wn];
873 if (MultiByteToWideChar (CP_UTF8, 0, filename, -1, buffer, wn) == 0) {
875 return Kumu::RESULT_FAIL;
878 m_Handle = ::CreateFileW(buffer,
879 (GENERIC_WRITE|GENERIC_READ), // open for reading
880 FILE_SHARE_READ, // share for reading
882 OPEN_ALWAYS, // don't truncate existing
883 FILE_ATTRIBUTE_NORMAL, // normal file
884 NULL // no template file
889 ::SetErrorMode(prev);
891 if ( m_Handle == INVALID_HANDLE_VALUE )
892 return Kumu::RESULT_FILEOPEN;
894 m_IOVec = new h__iovec;
895 return Kumu::RESULT_OK;
900 Kumu::FileWriter::Writev(ui32_t* bytes_written)
902 assert( ! m_IOVec.empty() );
903 register h__iovec* iov = m_IOVec;
906 if ( bytes_written == 0 )
907 bytes_written = &tmp_int;
909 if ( m_Handle == INVALID_HANDLE_VALUE )
910 return Kumu::RESULT_STATE;
913 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
914 Result_t result = Kumu::RESULT_OK;
916 // AFAIK, there is no writev() equivalent in the win32 API
917 for ( register int i = 0; i < iov->m_Count; i++ )
919 ui32_t tmp_count = 0;
920 BOOL wr_result = ::WriteFile(m_Handle,
921 iov->m_iovec[i].iov_base,
922 iov->m_iovec[i].iov_len,
926 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
928 result = Kumu::RESULT_WRITEFAIL;
932 MaybeHash (iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
933 *bytes_written += tmp_count;
936 ::SetErrorMode(prev);
937 iov->m_Count = 0; // error nor not, all is lost
944 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
949 if ( bytes_written == 0 )
950 bytes_written = &tmp_int;
952 if ( m_Handle == INVALID_HANDLE_VALUE )
953 return Kumu::RESULT_STATE;
955 // suppress popup window on error
956 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
957 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
958 ::SetErrorMode(prev);
960 if ( result == 0 || *bytes_written != buf_len )
961 return Kumu::RESULT_WRITEFAIL;
963 MaybeHash (buf, buf_len);
965 return Kumu::RESULT_OK;
969 //------------------------------------------------------------------------------------------
974 Kumu::FileReader::OpenRead(const char* filename) const
976 KM_TEST_NULL_STR_L(filename);
977 const_cast<FileReader*>(this)->m_Filename = filename;
978 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
979 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
984 Kumu::FileReader::Close() const
986 if ( m_Handle == -1L )
987 return RESULT_FILEOPEN;
990 const_cast<FileReader*>(this)->m_Handle = -1L;
996 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
998 if ( m_Handle == -1L )
999 return RESULT_FILEOPEN;
1001 if ( lseek(m_Handle, position, whence) == -1L )
1002 return RESULT_BADSEEK;
1009 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1011 KM_TEST_NULL_L(pos);
1013 if ( m_Handle == -1L )
1014 return RESULT_FILEOPEN;
1016 Kumu::fpos_t tmp_pos;
1018 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1019 return RESULT_READFAIL;
1027 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1029 KM_TEST_NULL_L(buf);
1030 i32_t tmp_count = 0;
1033 if ( read_count == 0 )
1034 read_count = &tmp_int;
1038 if ( m_Handle == -1L )
1039 return RESULT_FILEOPEN;
1041 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1042 return RESULT_READFAIL;
1044 *read_count = tmp_count;
1045 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1049 //------------------------------------------------------------------------------------------
1054 Kumu::FileWriter::OpenWrite(const char* filename)
1056 KM_TEST_NULL_STR_L(filename);
1057 m_Filename = filename;
1058 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
1060 if ( m_Handle == -1L )
1062 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
1063 return RESULT_FILEOPEN;
1066 m_IOVec = new h__iovec;
1072 Kumu::FileWriter::OpenModify(const char* filename)
1074 KM_TEST_NULL_STR_L(filename);
1075 m_Filename = filename;
1076 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
1078 if ( m_Handle == -1L )
1080 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
1081 return RESULT_FILEOPEN;
1084 m_IOVec = new h__iovec;
1090 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1092 assert( ! m_IOVec.empty() );
1093 register h__iovec* iov = m_IOVec;
1096 if ( bytes_written == 0 )
1097 bytes_written = &tmp_int;
1099 if ( m_Handle == -1L )
1100 return RESULT_STATE;
1103 for ( int i = 0; i < iov->m_Count; i++ )
1104 total_size += iov->m_iovec[i].iov_len;
1106 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1108 if ( write_size == -1L || write_size != total_size )
1109 return RESULT_WRITEFAIL;
1111 for (int i = 0; i < iov->m_Count; ++i) {
1112 MaybeHash (iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1116 *bytes_written = write_size;
1122 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1124 KM_TEST_NULL_L(buf);
1127 if ( bytes_written == 0 )
1128 bytes_written = &tmp_int;
1130 if ( m_Handle == -1L )
1131 return RESULT_STATE;
1133 int write_size = write(m_Handle, buf, buf_len);
1134 MaybeHash (buf, buf_len);
1136 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1137 return RESULT_WRITEFAIL;
1139 *bytes_written = write_size;
1146 //------------------------------------------------------------------------------------------
1151 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1154 ui32_t read_size = 0;
1158 KM_TEST_NULL_STR_L(filename);
1160 Result_t result = File.OpenRead(filename);
1162 if ( KM_SUCCESS(result) )
1164 fsize = File.Size();
1166 if ( fsize > (Kumu::fpos_t)max_size )
1168 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1169 return RESULT_ALLOC;
1174 DefaultLogSink().Error("%s: zero file size\n", filename);
1175 return RESULT_READFAIL;
1178 result = ReadBuf.Capacity((ui32_t)fsize);
1181 if ( KM_SUCCESS(result) )
1182 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1184 if ( KM_SUCCESS(result) )
1185 outString.assign((const char*)ReadBuf.RoData(), read_size);
1193 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1196 ui32_t write_count = 0;
1197 KM_TEST_NULL_STR_L(filename);
1199 Result_t result = File.OpenWrite(filename);
1201 if ( KM_SUCCESS(result) )
1202 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1207 //------------------------------------------------------------------------------------------
1212 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1215 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1216 Result_t result = Buffer.Capacity(file_size);
1218 if ( KM_SUCCESS(result) )
1220 ui32_t read_count = 0;
1223 result = Reader.OpenRead(Filename.c_str());
1225 if ( KM_SUCCESS(result) )
1226 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1228 if ( KM_SUCCESS(result) )
1230 assert(file_size == read_count);
1231 Buffer.Length(read_count);
1232 MemIOReader MemReader(&Buffer);
1233 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1242 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1245 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1247 if ( KM_SUCCESS(result) )
1249 ui32_t write_count = 0;
1251 MemIOWriter MemWriter(&Buffer);
1253 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1255 if ( KM_SUCCESS(result) )
1257 Buffer.Length(MemWriter.Length());
1258 result = Writer.OpenWrite(Filename.c_str());
1261 if ( KM_SUCCESS(result) )
1262 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1268 //------------------------------------------------------------------------------------------
1273 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1275 ui32_t file_size = FileSize(Filename);
1276 Result_t result = Buffer.Capacity(file_size);
1278 if ( KM_SUCCESS(result) )
1280 ui32_t read_count = 0;
1283 result = Reader.OpenRead(Filename.c_str());
1285 if ( KM_SUCCESS(result) )
1286 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1288 if ( KM_SUCCESS(result) )
1290 if ( file_size != read_count)
1291 return RESULT_READFAIL;
1293 Buffer.Length(read_count);
1302 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1304 ui32_t write_count = 0;
1307 Result_t result = Writer.OpenWrite(Filename.c_str());
1309 if ( KM_SUCCESS(result) )
1310 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1312 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1313 return RESULT_WRITEFAIL;
1318 //------------------------------------------------------------------------------------------
1321 Kumu::DirScanner::DirScanner()
1327 Kumu::DirScanner::Open (const char* filename)
1329 KM_TEST_NULL_L (filename);
1331 if (!boost::filesystem::is_directory(filename)) {
1332 return RESULT_NOT_FOUND;
1335 _iterator = boost::filesystem::directory_iterator (filename);
1340 Kumu::DirScanner::GetNext (char* filename)
1342 KM_TEST_NULL_L (filename);
1344 if (_iterator == boost::filesystem::directory_iterator()) {
1345 return RESULT_ENDOFFILE;
1348 #if BOOST_FILESYSTEM_VERSION == 3
1349 std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1351 std::string f = boost::filesystem::path(*_iterator).filename();
1353 strncpy (filename, f.c_str(), MaxFilePath);
1358 //------------------------------------------------------------------------------------------
1361 // Attention Windows users: make sure to use the proper separator character
1362 // with these functions.
1365 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1368 Kumu::CreateDirectoriesInPath(const std::string& Path)
1370 bool abs = PathIsAbsolute(Path);
1371 PathCompList_t PathComps, TmpPathComps;
1373 PathToComponents(Path, PathComps);
1375 while ( ! PathComps.empty() )
1377 TmpPathComps.push_back(PathComps.front());
1378 PathComps.pop_front();
1379 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1381 if ( ! PathIsDirectory(tmp_path) )
1384 if ( _mkdir(tmp_path.c_str()) != 0 )
1386 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1389 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1390 tmp_path.c_str(), strerror(errno));
1391 return RESULT_DIR_CREATE;
1402 Kumu::DeleteFile(const std::string& filename)
1404 if ( _unlink(filename.c_str()) == 0 )
1410 case ENOTDIR: return RESULT_NOTAFILE;
1415 case EPERM: return RESULT_NO_PERM;
1418 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1424 h__DeletePath(const std::string& pathname)
1426 if ( pathname.empty() )
1427 return RESULT_NULL_STR;
1429 Result_t result = RESULT_OK;
1431 if ( ! PathIsDirectory(pathname) )
1433 result = DeleteFile(pathname);
1439 char next_file[Kumu::MaxFilePath];
1440 result = TestDir.Open(pathname.c_str());
1442 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1444 if ( next_file[0] == '.' )
1446 if ( next_file[1] == 0 )
1447 continue; // don't delete 'this'
1449 if ( next_file[1] == '.' && next_file[2] == 0 )
1450 continue; // don't delete 'this' parent
1453 result = h__DeletePath(pathname + std::string("/") + next_file);
1457 if ( _rmdir(pathname.c_str()) != 0 )
1463 result = RESULT_NOTAFILE;
1470 result = RESULT_NO_PERM;
1474 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1475 result = RESULT_FAIL;
1485 Kumu::DeletePath(const std::string& pathname)
1487 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1488 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1489 return h__DeletePath(c_pathname);
1493 //------------------------------------------------------------------------------------------
1498 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1501 ULARGE_INTEGER lTotalNumberOfBytes;
1502 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1504 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1506 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1507 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1510 HRESULT LastError = ::GetLastError();
1512 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1517 if ( statfs(path.c_str(), &s) == 0 )
1519 if ( s.f_blocks < 1 )
1521 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1522 path.c_str(), s.f_blocks);
1526 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1527 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1534 case ENOTDIR: return RESULT_NOTAFILE;
1535 case EACCES: return RESULT_NO_PERM;
1538 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1545 // end KM_fileio.cpp