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
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 max_size)
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 max_size)
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 //------------------------------------------------------------------------------------------
1215 // Win32 directory scanner
1220 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1225 Kumu::DirScanner::Open(const char* filename)
1227 KM_TEST_NULL_STR_L(filename);
1229 // we need to append a '*' to read the entire directory
1230 ui32_t fn_len = strlen(filename);
1231 char* tmp_file = (char*)malloc(fn_len + 8);
1233 if ( tmp_file == 0 )
1234 return RESULT_ALLOC;
1236 strcpy(tmp_file, filename);
1237 char* p = &tmp_file[fn_len] - 1;
1239 if ( *p != '/' && *p != '\\' )
1249 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1250 Result_t result = RESULT_OK;
1252 if ( m_Handle == -1 )
1253 result = RESULT_NOT_FOUND;
1262 Kumu::DirScanner::Close()
1264 if ( m_Handle == -1 )
1265 return RESULT_FILEOPEN;
1267 if ( _findclose((long)m_Handle) == -1 )
1275 // This sets filename param to the same per-instance buffer every time, so
1276 // the value will change on the next call
1278 Kumu::DirScanner::GetNext(char* filename)
1280 KM_TEST_NULL_L(filename);
1282 if ( m_Handle == -1 )
1283 return RESULT_FILEOPEN;
1285 if ( m_FileInfo.name[0] == '\0' )
1286 return RESULT_ENDOFFILE;
1288 strncpy(filename, m_FileInfo.name, MaxFilePath);
1289 Result_t result = RESULT_OK;
1291 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1293 m_FileInfo.name[0] = '\0';
1295 if ( errno != ENOENT )
1296 result = RESULT_FAIL;
1305 // POSIX directory scanner
1308 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1312 Kumu::DirScanner::Open(const char* filename)
1314 KM_TEST_NULL_STR_L(filename);
1316 Result_t result = RESULT_OK;
1318 if ( ( m_Handle = opendir(filename) ) == NULL )
1324 result = RESULT_NOTAFILE;
1326 result = RESULT_NO_PERM;
1329 result = RESULT_PARAM;
1332 result = RESULT_STATE;
1334 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", filename, strerror(errno));
1335 result = RESULT_FAIL;
1345 Kumu::DirScanner::Close()
1347 if ( m_Handle == NULL )
1348 return RESULT_FILEOPEN;
1350 if ( closedir(m_Handle) == -1 ) {
1355 return RESULT_STATE;
1357 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1369 Kumu::DirScanner::GetNext(char* filename)
1371 KM_TEST_NULL_L(filename);
1373 if ( m_Handle == NULL )
1374 return RESULT_FILEOPEN;
1376 struct dirent* entry;
1380 if ( ( entry = readdir(m_Handle)) == NULL )
1381 return RESULT_ENDOFFILE;
1386 strncpy(filename, entry->d_name, MaxFilePath);
1394 //------------------------------------------------------------------------------------------
1397 // Attention Windows users: make sure to use the proper separator character
1398 // with these functions.
1401 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1404 Kumu::CreateDirectoriesInPath(const std::string& Path)
1406 bool abs = PathIsAbsolute(Path);
1407 PathCompList_t PathComps, TmpPathComps;
1409 PathToComponents(Path, PathComps);
1411 while ( ! PathComps.empty() )
1413 TmpPathComps.push_back(PathComps.front());
1414 PathComps.pop_front();
1415 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1417 if ( ! PathIsDirectory(tmp_path) )
1420 if ( _mkdir(tmp_path.c_str()) != 0 )
1422 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1425 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1426 tmp_path.c_str(), strerror(errno));
1427 return RESULT_DIR_CREATE;
1438 Kumu::DeleteFile(const std::string& filename)
1440 if ( _unlink(filename.c_str()) == 0 )
1446 case ENOTDIR: return RESULT_NOTAFILE;
1451 case EPERM: return RESULT_NO_PERM;
1454 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1460 h__DeletePath(const std::string& pathname)
1462 if ( pathname.empty() )
1463 return RESULT_NULL_STR;
1465 Result_t result = RESULT_OK;
1467 if ( ! PathIsDirectory(pathname) )
1469 result = DeleteFile(pathname);
1475 char next_file[Kumu::MaxFilePath];
1476 result = TestDir.Open(pathname.c_str());
1478 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1480 if ( next_file[0] == '.' )
1482 if ( next_file[1] == 0 )
1483 continue; // don't delete 'this'
1485 if ( next_file[1] == '.' && next_file[2] == 0 )
1486 continue; // don't delete 'this' parent
1489 result = h__DeletePath(pathname + std::string("/") + next_file);
1493 if ( _rmdir(pathname.c_str()) != 0 )
1499 result = RESULT_NOTAFILE;
1506 result = RESULT_NO_PERM;
1510 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1511 result = RESULT_FAIL;
1521 Kumu::DeletePath(const std::string& pathname)
1523 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1524 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1525 return h__DeletePath(c_pathname);
1529 //------------------------------------------------------------------------------------------
1534 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1537 ULARGE_INTEGER lTotalNumberOfBytes;
1538 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1540 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1542 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1543 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1546 HRESULT LastError = ::GetLastError();
1548 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1553 if ( statfs(path.c_str(), &s) == 0 )
1555 if ( s.f_blocks < 1 )
1557 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1558 path.c_str(), s.f_blocks);
1562 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1563 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1570 case ENOTDIR: return RESULT_NOTAFILE;
1571 case EACCES: return RESULT_NO_PERM;
1574 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1581 // end KM_fileio.cpp