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>
41 #define _getcwd getcwd
42 #define _unlink unlink
49 typedef struct _stati64 fstat_t;
53 // win32 has WriteFileGather() and ReadFileScatter() but they
54 // demand page alignment and page sizing, making them unsuitable
55 // for use with arbitrary buffer sizes.
57 char* iov_base; // stupid iovec uses char*
61 # if defined(__linux__)
62 # include <sys/statfs.h>
64 # include <sys/param.h>
65 # include <sys/mount.h>
70 typedef struct stat fstat_t;
75 split(const std::string& str, char separator, std::list<std::string>& components)
77 const char* pstr = str.c_str();
78 const char* r = strchr(pstr, separator);
86 tmp_str.assign(pstr, (r - pstr));
87 components.push_back(tmp_str);
91 r = strchr(pstr, separator);
94 if( strlen(pstr) > 0 )
95 components.push_back(std::string(pstr));
100 static Kumu::Result_t
101 do_stat(const char* path, fstat_t* stat_info)
103 KM_TEST_NULL_STR_L(path);
104 KM_TEST_NULL_L(stat_info);
106 Kumu::Result_t result = Kumu::RESULT_OK;
109 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
111 if ( _stati64(path, stat_info) == (__int64)-1 )
112 result = Kumu::RESULT_FILEOPEN;
114 ::SetErrorMode( prev );
116 if ( stat(path, stat_info) == -1L )
117 result = Kumu::RESULT_FILEOPEN;
119 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
120 result = Kumu::RESULT_FILEOPEN;
129 static Kumu::Result_t
130 do_fstat(FileHandle handle, fstat_t* stat_info)
132 KM_TEST_NULL_L(stat_info);
134 Kumu::Result_t result = Kumu::RESULT_OK;
136 if ( fstat(handle, stat_info) == -1L )
137 result = Kumu::RESULT_FILEOPEN;
139 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
140 result = Kumu::RESULT_FILEOPEN;
150 Kumu::PathExists(const std::string& pathname)
152 if ( pathname.empty() )
157 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
165 Kumu::PathIsFile(const std::string& pathname)
167 if ( pathname.empty() )
172 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
174 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
184 Kumu::PathIsDirectory(const std::string& pathname)
186 if ( pathname.empty() )
191 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
193 if ( info.st_mode & S_IFDIR )
202 Kumu::FileSize(const std::string& pathname)
204 if ( pathname.empty() )
209 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
211 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
212 return(info.st_size);
219 static PathCompList_t&
220 s_PathMakeCanonical(PathCompList_t& CList, bool is_absolute)
222 PathCompList_t::iterator ci, ri; // component and removal iterators
224 for ( ci = CList.begin(); ci != CList.end(); ci++ )
226 if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
231 else if ( *ci == ".." && ci != CList.begin() )
250 Kumu::PathMakeCanonical(const std::string& Path, char separator)
252 PathCompList_t CList;
253 bool is_absolute = PathIsAbsolute(Path, separator);
254 s_PathMakeCanonical(PathToComponents(Path, CList, separator), is_absolute);
257 return ComponentsToAbsolutePath(CList, separator);
259 return ComponentsToPath(CList, separator);
264 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
266 return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
270 Kumu::PathCompList_t&
271 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
273 split(Path, separator, CList);
279 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
284 PathCompList_t::const_iterator ci = CList.begin();
285 std::string out_path = *ci;
287 for ( ci++; ci != CList.end(); ci++ )
288 out_path += separator + *ci;
295 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
297 std::string out_path;
300 out_path = separator;
303 PathCompList_t::const_iterator ci;
305 for ( ci = CList.begin(); ci != CList.end(); ci++ )
306 out_path += separator + *ci;
314 Kumu::PathHasComponents(const std::string& Path, char separator)
316 if ( strchr(Path.c_str(), separator) == 0 )
324 Kumu::PathIsAbsolute(const std::string& Path, char separator)
329 if ( Path[0] == separator)
337 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
341 std::string out_path;
342 out_path = separator;
346 if ( PathIsAbsolute(Path, separator) )
349 char cwd_buf [MaxFilePath];
350 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
352 DefaultLogSink().Error("Error retrieving current working directory.");
356 PathCompList_t CList;
357 PathToComponents(cwd_buf, CList);
358 CList.push_back(Path);
360 return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, true), separator);
365 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
367 size_t pos = Path.find(Parent);
369 if ( pos == 0 ) // Parent found at offset 0
370 return Path.substr(Parent.size()+1);
377 Kumu::PathBasename(const std::string& Path, char separator)
379 PathCompList_t CList;
380 PathToComponents(Path, CList, separator);
390 Kumu::PathDirname(const std::string& Path, char separator)
392 PathCompList_t CList;
393 bool is_absolute = PathIsAbsolute(Path, separator);
394 PathToComponents(Path, CList, separator);
397 return is_absolute ? "/" : "";
402 return ComponentsToAbsolutePath(CList, separator);
404 return ComponentsToPath(CList, separator);
409 Kumu::PathGetExtension(const std::string& Path)
411 std::string Basename = PathBasename(Path);
412 const char* p = strrchr(Basename.c_str(), '.');
422 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
424 std::string Basename = PathBasename(Path);
425 const char* p = strrchr(Basename.c_str(), '.');
428 Basename = Basename.substr(0, p - Basename.c_str());
430 if ( Extension.empty() )
433 return Basename + "." + Extension;
438 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
440 return Path1 + separator + Path2;
445 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
447 return Path1 + separator + Path2 + separator + Path3;
452 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
453 const std::string& Path3, const std::string& Path4, char separator)
455 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
460 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
461 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
463 PathList_t::const_iterator si;
464 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
466 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
468 if ( one_shot && ! FoundPaths.empty() )
477 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
478 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
480 char name_buf[MaxFilePath];
483 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
485 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
487 if ( name_buf[0] == '.' ) continue; // no hidden files
488 std::string tmp_path = SearchDir + separator + name_buf;
490 if ( PathIsDirectory(tmp_path.c_str()) )
491 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
493 else if ( Pattern.Match(name_buf) )
495 FoundPaths.push_back(SearchDir + separator + name_buf);
509 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
511 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
516 regerror(result, &m_regex, buf, 128);
517 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
522 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
523 m_regex = rhs.m_regex;
526 Kumu::PathMatchRegex::~PathMatchRegex() {
531 Kumu::PathMatchRegex::Match(const std::string& s) const {
532 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
538 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
540 std::string regex; // convert glob to regex
542 for ( const char* p = glob.c_str(); *p != 0; p++ )
546 case '.': regex += "\\."; break;
547 case '*': regex += ".*"; break;
548 case '?': regex += ".?"; break;
549 default: regex += *p;
554 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
559 regerror(result, &m_regex, buf, 128);
560 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
565 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
566 m_regex = rhs.m_regex;
569 Kumu::PathMatchGlob::~PathMatchGlob() {
574 Kumu::PathMatchGlob::Match(const std::string& s) const {
575 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
580 //------------------------------------------------------------------------------------------
581 // portable aspects of the file classes
583 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
586 class Kumu::FileWriter::h__iovec
590 struct iovec m_iovec[IOVecMaxEntries];
591 h__iovec() : m_Count(0) {}
598 Kumu::FileReader::Size() const
601 return FileSize(m_Filename.c_str());
605 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
607 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
608 return(info.st_size);
615 // these are declared here instead of in the header file
616 // because we have a mem_ptr that is managing a hidden class
617 Kumu::FileWriter::FileWriter() {}
618 Kumu::FileWriter::~FileWriter() {}
622 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
624 assert( ! m_IOVec.empty() );
625 register h__iovec* iov = m_IOVec;
628 if ( iov->m_Count >= IOVecMaxEntries )
630 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
632 return RESULT_WRITEFAIL;
635 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
636 iov->m_iovec[iov->m_Count].iov_len = buf_len;
644 //------------------------------------------------------------------------------------------
648 Kumu::FileReader::OpenRead(const char* filename) const
650 KM_TEST_NULL_STR_L(filename);
651 const_cast<FileReader*>(this)->m_Filename = filename;
653 // suppress popup window on error
654 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
656 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename,
657 (GENERIC_READ), // open for reading
658 FILE_SHARE_READ, // share for reading
660 OPEN_EXISTING, // read
661 FILE_ATTRIBUTE_NORMAL, // normal file
662 NULL // no template file
665 ::SetErrorMode(prev);
667 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
668 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
673 Kumu::FileReader::Close() const
675 if ( m_Handle == INVALID_HANDLE_VALUE )
676 return Kumu::RESULT_FILEOPEN;
678 // suppress popup window on error
679 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
680 BOOL result = ::CloseHandle(m_Handle);
681 ::SetErrorMode(prev);
682 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
684 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
689 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
691 if ( m_Handle == INVALID_HANDLE_VALUE )
692 return Kumu::RESULT_STATE;
695 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
696 in.QuadPart = position;
697 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
698 HRESULT LastError = GetLastError();
699 ::SetErrorMode(prev);
701 if ( (LastError != NO_ERROR
702 && (in.LowPart == INVALID_SET_FILE_POINTER
703 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
704 return Kumu::RESULT_READFAIL;
706 return Kumu::RESULT_OK;
711 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
715 if ( m_Handle == INVALID_HANDLE_VALUE )
716 return Kumu::RESULT_FILEOPEN;
719 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
720 in.QuadPart = (__int64)0;
721 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
722 HRESULT LastError = GetLastError();
723 ::SetErrorMode(prev);
725 if ( (LastError != NO_ERROR
726 && (in.LowPart == INVALID_SET_FILE_POINTER
727 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
728 return Kumu::RESULT_READFAIL;
730 *pos = (Kumu::fpos_t)in.QuadPart;
731 return Kumu::RESULT_OK;
736 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
739 Result_t result = Kumu::RESULT_OK;
743 if ( read_count == 0 )
744 read_count = &tmp_int;
748 if ( m_Handle == INVALID_HANDLE_VALUE )
749 return Kumu::RESULT_FILEOPEN;
751 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
752 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
753 result = Kumu::RESULT_READFAIL;
755 ::SetErrorMode(prev);
757 if ( tmp_count == 0 ) /* EOF */
758 result = Kumu::RESULT_ENDOFFILE;
760 if ( KM_SUCCESS(result) )
761 *read_count = tmp_count;
768 //------------------------------------------------------------------------------------------
773 Kumu::FileWriter::OpenWrite(const char* filename)
775 KM_TEST_NULL_STR_L(filename);
776 m_Filename = filename;
778 // suppress popup window on error
779 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
781 m_Handle = ::CreateFileA(filename,
782 (GENERIC_WRITE|GENERIC_READ), // open for reading
783 FILE_SHARE_READ, // share for reading
785 CREATE_ALWAYS, // overwrite (beware!)
786 FILE_ATTRIBUTE_NORMAL, // normal file
787 NULL // no template file
790 ::SetErrorMode(prev);
792 if ( m_Handle == INVALID_HANDLE_VALUE )
793 return Kumu::RESULT_FILEOPEN;
795 m_IOVec = new h__iovec;
796 return Kumu::RESULT_OK;
801 Kumu::FileWriter::Writev(ui32_t* bytes_written)
803 assert( ! m_IOVec.empty() );
804 register h__iovec* iov = m_IOVec;
807 if ( bytes_written == 0 )
808 bytes_written = &tmp_int;
810 if ( m_Handle == INVALID_HANDLE_VALUE )
811 return Kumu::RESULT_STATE;
814 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
815 Result_t result = Kumu::RESULT_OK;
817 // AFAIK, there is no writev() equivalent in the win32 API
818 for ( register int i = 0; i < iov->m_Count; i++ )
820 ui32_t tmp_count = 0;
821 BOOL wr_result = ::WriteFile(m_Handle,
822 iov->m_iovec[i].iov_base,
823 iov->m_iovec[i].iov_len,
827 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
829 result = Kumu::RESULT_WRITEFAIL;
833 *bytes_written += tmp_count;
836 ::SetErrorMode(prev);
837 iov->m_Count = 0; // error nor not, all is lost
844 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
849 if ( bytes_written == 0 )
850 bytes_written = &tmp_int;
852 if ( m_Handle == INVALID_HANDLE_VALUE )
853 return Kumu::RESULT_STATE;
855 // suppress popup window on error
856 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
857 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
858 ::SetErrorMode(prev);
860 if ( result == 0 || *bytes_written != buf_len )
861 return Kumu::RESULT_WRITEFAIL;
863 return Kumu::RESULT_OK;
867 //------------------------------------------------------------------------------------------
872 Kumu::FileReader::OpenRead(const char* filename) const
874 KM_TEST_NULL_STR_L(filename);
875 const_cast<FileReader*>(this)->m_Filename = filename;
876 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
877 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
882 Kumu::FileReader::Close() const
884 if ( m_Handle == -1L )
885 return RESULT_FILEOPEN;
888 const_cast<FileReader*>(this)->m_Handle = -1L;
894 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
896 if ( m_Handle == -1L )
897 return RESULT_FILEOPEN;
899 if ( lseek(m_Handle, position, whence) == -1L )
900 return RESULT_BADSEEK;
907 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
911 if ( m_Handle == -1L )
912 return RESULT_FILEOPEN;
914 Kumu::fpos_t tmp_pos;
916 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
917 return RESULT_READFAIL;
925 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
931 if ( read_count == 0 )
932 read_count = &tmp_int;
936 if ( m_Handle == -1L )
937 return RESULT_FILEOPEN;
939 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
940 return RESULT_READFAIL;
942 *read_count = tmp_count;
943 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
947 //------------------------------------------------------------------------------------------
952 Kumu::FileWriter::OpenWrite(const char* filename)
954 KM_TEST_NULL_STR_L(filename);
955 m_Filename = filename;
956 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
958 if ( m_Handle == -1L )
960 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
961 return RESULT_FILEOPEN;
964 m_IOVec = new h__iovec;
970 Kumu::FileWriter::OpenModify(const char* filename)
972 KM_TEST_NULL_STR_L(filename);
973 m_Filename = filename;
974 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
976 if ( m_Handle == -1L )
978 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
979 return RESULT_FILEOPEN;
982 m_IOVec = new h__iovec;
988 Kumu::FileWriter::Writev(ui32_t* bytes_written)
990 assert( ! m_IOVec.empty() );
991 register h__iovec* iov = m_IOVec;
994 if ( bytes_written == 0 )
995 bytes_written = &tmp_int;
997 if ( m_Handle == -1L )
1001 for ( int i = 0; i < iov->m_Count; i++ )
1002 total_size += iov->m_iovec[i].iov_len;
1004 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1006 if ( write_size == -1L || write_size != total_size )
1007 return RESULT_WRITEFAIL;
1010 *bytes_written = write_size;
1016 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1018 KM_TEST_NULL_L(buf);
1021 if ( bytes_written == 0 )
1022 bytes_written = &tmp_int;
1024 if ( m_Handle == -1L )
1025 return RESULT_STATE;
1027 int write_size = write(m_Handle, buf, buf_len);
1029 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1030 return RESULT_WRITEFAIL;
1032 *bytes_written = write_size;
1039 //------------------------------------------------------------------------------------------
1044 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1047 ui32_t read_size = 0;
1051 KM_TEST_NULL_STR_L(filename);
1053 Result_t result = File.OpenRead(filename);
1055 if ( KM_SUCCESS(result) )
1057 fsize = File.Size();
1059 if ( fsize > (Kumu::fpos_t)max_size )
1061 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1062 return RESULT_ALLOC;
1067 DefaultLogSink().Error("%s: zero file size\n", filename);
1068 return RESULT_READFAIL;
1071 result = ReadBuf.Capacity((ui32_t)fsize);
1074 if ( KM_SUCCESS(result) )
1075 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1077 if ( KM_SUCCESS(result) )
1078 outString.assign((const char*)ReadBuf.RoData(), read_size);
1086 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1089 ui32_t write_count = 0;
1090 KM_TEST_NULL_STR_L(filename);
1092 Result_t result = File.OpenWrite(filename);
1094 if ( KM_SUCCESS(result) )
1095 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1100 //------------------------------------------------------------------------------------------
1105 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1108 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1109 Result_t result = Buffer.Capacity(file_size);
1111 if ( KM_SUCCESS(result) )
1113 ui32_t read_count = 0;
1116 result = Reader.OpenRead(Filename.c_str());
1118 if ( KM_SUCCESS(result) )
1119 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1121 if ( KM_SUCCESS(result) )
1123 assert(file_size == read_count);
1124 Buffer.Length(read_count);
1125 MemIOReader MemReader(&Buffer);
1126 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1135 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1138 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1140 if ( KM_SUCCESS(result) )
1142 ui32_t write_count = 0;
1144 MemIOWriter MemWriter(&Buffer);
1146 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1148 if ( KM_SUCCESS(result) )
1150 Buffer.Length(MemWriter.Length());
1151 result = Writer.OpenWrite(Filename.c_str());
1154 if ( KM_SUCCESS(result) )
1155 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1161 //------------------------------------------------------------------------------------------
1166 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1168 ui32_t file_size = FileSize(Filename);
1169 Result_t result = Buffer.Capacity(file_size);
1171 if ( KM_SUCCESS(result) )
1173 ui32_t read_count = 0;
1176 result = Reader.OpenRead(Filename.c_str());
1178 if ( KM_SUCCESS(result) )
1179 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1181 if ( KM_SUCCESS(result) )
1183 if ( file_size != read_count)
1184 return RESULT_READFAIL;
1186 Buffer.Length(read_count);
1195 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1197 ui32_t write_count = 0;
1200 Result_t result = Writer.OpenWrite(Filename.c_str());
1202 if ( KM_SUCCESS(result) )
1203 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1205 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1206 return RESULT_WRITEFAIL;
1211 //------------------------------------------------------------------------------------------
1214 Kumu::DirScanner::DirScanner()
1220 Kumu::DirScanner::Open (const char* filename)
1222 KM_TEST_NULL_L (filename);
1224 if (!boost::filesystem::is_directory(filename)) {
1225 return RESULT_NOT_FOUND;
1228 _iterator = boost::filesystem::directory_iterator (filename);
1233 Kumu::DirScanner::GetNext (char* filename)
1235 KM_TEST_NULL_L (filename);
1237 if (_iterator == boost::filesystem::directory_iterator()) {
1238 return RESULT_ENDOFFILE;
1241 strncpy (filename, boost::filesystem::path(*_iterator).filename().generic_string().c_str(), MaxFilePath);
1246 //------------------------------------------------------------------------------------------
1249 // Attention Windows users: make sure to use the proper separator character
1250 // with these functions.
1253 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1256 Kumu::CreateDirectoriesInPath(const std::string& Path)
1258 bool abs = PathIsAbsolute(Path);
1259 PathCompList_t PathComps, TmpPathComps;
1261 PathToComponents(Path, PathComps);
1263 while ( ! PathComps.empty() )
1265 TmpPathComps.push_back(PathComps.front());
1266 PathComps.pop_front();
1267 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1269 if ( ! PathIsDirectory(tmp_path) )
1272 if ( _mkdir(tmp_path.c_str()) != 0 )
1274 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1277 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1278 tmp_path.c_str(), strerror(errno));
1279 return RESULT_DIR_CREATE;
1290 Kumu::DeleteFile(const std::string& filename)
1292 if ( _unlink(filename.c_str()) == 0 )
1298 case ENOTDIR: return RESULT_NOTAFILE;
1303 case EPERM: return RESULT_NO_PERM;
1306 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1312 h__DeletePath(const std::string& pathname)
1314 if ( pathname.empty() )
1315 return RESULT_NULL_STR;
1317 Result_t result = RESULT_OK;
1319 if ( ! PathIsDirectory(pathname) )
1321 result = DeleteFile(pathname);
1327 char next_file[Kumu::MaxFilePath];
1328 result = TestDir.Open(pathname.c_str());
1330 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1332 if ( next_file[0] == '.' )
1334 if ( next_file[1] == 0 )
1335 continue; // don't delete 'this'
1337 if ( next_file[1] == '.' && next_file[2] == 0 )
1338 continue; // don't delete 'this' parent
1341 result = h__DeletePath(pathname + std::string("/") + next_file);
1345 if ( _rmdir(pathname.c_str()) != 0 )
1351 result = RESULT_NOTAFILE;
1358 result = RESULT_NO_PERM;
1362 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1363 result = RESULT_FAIL;
1373 Kumu::DeletePath(const std::string& pathname)
1375 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1376 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1377 return h__DeletePath(c_pathname);
1381 //------------------------------------------------------------------------------------------
1386 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1389 ULARGE_INTEGER lTotalNumberOfBytes;
1390 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1392 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1394 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1395 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1398 HRESULT LastError = ::GetLastError();
1400 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1405 if ( statfs(path.c_str(), &s) == 0 )
1407 if ( s.f_blocks < 1 )
1409 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1410 path.c_str(), s.f_blocks);
1414 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1415 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1422 case ENOTDIR: return RESULT_NOTAFILE;
1423 case EACCES: return RESULT_NO_PERM;
1426 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1433 // end KM_fileio.cpp