2 Copyright (c) 2004-2009, 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>
45 typedef struct _stati64 fstat_t;
49 // win32 has WriteFileGather() and ReadFileScatter() but they
50 // demand page alignment and page sizing, making them unsuitable
51 // for use with arbitrary buffer sizes.
53 char* iov_base; // stupid iovec uses char*
57 # if defined(__linux__)
58 # include <sys/statfs.h>
60 # include <sys/param.h>
61 # include <sys/mount.h>
66 typedef struct stat fstat_t;
71 split(const std::string& str, char separator, std::list<std::string>& components)
73 const char* pstr = str.c_str();
74 const char* r = strchr(pstr, separator);
82 tmp_str.assign(pstr, (r - pstr));
83 components.push_back(tmp_str);
87 r = strchr(pstr, separator);
90 if( strlen(pstr) > 0 )
91 components.push_back(std::string(pstr));
97 do_stat(const char* path, fstat_t* stat_info)
99 KM_TEST_NULL_STR_L(path);
100 KM_TEST_NULL_L(stat_info);
102 Kumu::Result_t result = Kumu::RESULT_OK;
105 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
107 if ( _stati64(path, stat_info) == (__int64)-1 )
108 result = Kumu::RESULT_FILEOPEN;
110 ::SetErrorMode( prev );
112 if ( stat(path, stat_info) == -1L )
113 result = Kumu::RESULT_FILEOPEN;
115 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
116 result = Kumu::RESULT_FILEOPEN;
125 static Kumu::Result_t
126 do_fstat(FileHandle handle, fstat_t* stat_info)
128 KM_TEST_NULL_L(stat_info);
130 Kumu::Result_t result = Kumu::RESULT_OK;
132 if ( fstat(handle, stat_info) == -1L )
133 result = Kumu::RESULT_FILEOPEN;
135 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
136 result = Kumu::RESULT_FILEOPEN;
146 Kumu::PathExists(const std::string& pathname)
148 if ( pathname.empty() )
153 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
161 Kumu::PathIsFile(const std::string& pathname)
163 if ( pathname.empty() )
168 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
170 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
180 Kumu::PathIsDirectory(const std::string& pathname)
182 if ( pathname.empty() )
187 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
189 if ( info.st_mode & S_IFDIR )
198 Kumu::FileSize(const std::string& pathname)
200 if ( pathname.empty() )
205 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
207 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
208 return(info.st_size);
215 static PathCompList_t&
216 s_PathMakeCanonical(PathCompList_t& CList, bool is_absolute)
218 PathCompList_t::iterator ci, ri; // component and removal iterators
220 for ( ci = CList.begin(); ci != CList.end(); ci++ )
222 if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
227 else if ( *ci == ".." && ci != CList.begin() )
246 Kumu::PathMakeCanonical(const std::string& Path, char separator)
248 PathCompList_t CList;
249 bool is_absolute = PathIsAbsolute(Path, separator);
250 s_PathMakeCanonical(PathToComponents(Path, CList, separator), is_absolute);
253 return ComponentsToAbsolutePath(CList, separator);
255 return ComponentsToPath(CList, separator);
260 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
262 return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
266 Kumu::PathCompList_t&
267 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
269 split(Path, separator, CList);
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)
333 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
337 std::string out_path;
338 out_path = separator;
342 if ( PathIsAbsolute(Path, separator) )
345 char cwd_buf [MaxFilePath];
346 if ( getcwd(cwd_buf, MaxFilePath) == 0 )
348 DefaultLogSink().Error("Error retrieving current working directory.");
352 PathCompList_t CList;
353 PathToComponents(cwd_buf, CList);
354 CList.push_back(Path);
356 return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, true), separator);
361 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
363 size_t pos = Path.find(Parent);
365 if ( pos == 0 ) // Parent found at offset 0
366 return Path.substr(Parent.size()+1);
373 Kumu::PathBasename(const std::string& Path, char separator)
375 PathCompList_t CList;
376 PathToComponents(Path, CList, separator);
386 Kumu::PathDirname(const std::string& Path, char separator)
388 PathCompList_t CList;
389 bool is_absolute = PathIsAbsolute(Path, separator);
390 PathToComponents(Path, CList, separator);
393 return is_absolute ? "/" : "";
398 return ComponentsToAbsolutePath(CList, separator);
400 return ComponentsToPath(CList, separator);
405 Kumu::PathGetExtension(const std::string& Path)
407 std::string Basename = PathBasename(Path);
408 const char* p = strrchr(Basename.c_str(), '.');
418 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
420 std::string Basename = PathBasename(Path);
421 const char* p = strrchr(Basename.c_str(), '.');
424 Basename = Basename.substr(0, p - Basename.c_str());
426 if ( Extension.empty() )
429 return Basename + "." + Extension;
434 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
436 return Path1 + separator + Path2;
441 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
443 return Path1 + separator + Path2 + separator + Path3;
448 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
449 const std::string& Path3, const std::string& Path4, char separator)
451 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
456 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
457 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
459 PathList_t::const_iterator si;
460 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
462 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
464 if ( one_shot && ! FoundPaths.empty() )
473 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
474 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
476 char name_buf[MaxFilePath];
479 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
481 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
483 if ( name_buf[0] == '.' ) continue; // no hidden files
484 std::string tmp_path = SearchDir + separator + name_buf;
486 if ( PathIsDirectory(tmp_path.c_str()) )
487 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
489 else if ( Pattern.Match(name_buf) )
491 FoundPaths.push_back(SearchDir + separator + name_buf);
505 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
507 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
512 regerror(result, &m_regex, buf, 128);
513 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
518 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
519 m_regex = rhs.m_regex;
522 Kumu::PathMatchRegex::~PathMatchRegex() {
527 Kumu::PathMatchRegex::Match(const std::string& s) const {
528 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
534 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
536 std::string regex; // convert glob to regex
538 for ( const char* p = glob.c_str(); *p != 0; p++ )
542 case '.': regex += "\\."; break;
543 case '*': regex += ".*"; break;
544 case '?': regex += ".?"; break;
545 default: regex += *p;
550 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
555 regerror(result, &m_regex, buf, 128);
556 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
561 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
562 m_regex = rhs.m_regex;
565 Kumu::PathMatchGlob::~PathMatchGlob() {
570 Kumu::PathMatchGlob::Match(const std::string& s) const {
571 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
576 //------------------------------------------------------------------------------------------
577 // portable aspects of the file classes
579 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
582 class Kumu::FileWriter::h__iovec
586 struct iovec m_iovec[IOVecMaxEntries];
587 h__iovec() : m_Count(0) {}
594 Kumu::FileReader::Size() const
597 return FileSize(m_Filename.c_str());
601 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
603 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
604 return(info.st_size);
611 // these are declared here instead of in the header file
612 // because we have a mem_ptr that is managing a hidden class
613 Kumu::FileWriter::FileWriter() {}
614 Kumu::FileWriter::~FileWriter() {}
618 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
620 assert( ! m_IOVec.empty() );
621 register h__iovec* iov = m_IOVec;
624 if ( iov->m_Count >= IOVecMaxEntries )
626 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
628 return RESULT_WRITEFAIL;
631 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
632 iov->m_iovec[iov->m_Count].iov_len = buf_len;
640 //------------------------------------------------------------------------------------------
644 Kumu::FileReader::OpenRead(const char* filename) const
646 KM_TEST_NULL_STR_L(filename);
647 const_cast<FileReader*>(this)->m_Filename = filename;
649 // suppress popup window on error
650 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
652 const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
653 (GENERIC_READ), // open for reading
654 FILE_SHARE_READ, // share for reading
656 OPEN_EXISTING, // read
657 FILE_ATTRIBUTE_NORMAL, // normal file
658 NULL // no template file
661 ::SetErrorMode(prev);
663 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
664 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
669 Kumu::FileReader::Close() const
671 if ( m_Handle == INVALID_HANDLE_VALUE )
672 return Kumu::RESULT_FILEOPEN;
674 // suppress popup window on error
675 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
676 BOOL result = ::CloseHandle(m_Handle);
677 ::SetErrorMode(prev);
678 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
680 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
685 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
687 if ( m_Handle == INVALID_HANDLE_VALUE )
688 return Kumu::RESULT_STATE;
691 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
692 in.QuadPart = position;
693 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
694 HRESULT LastError = GetLastError();
695 ::SetErrorMode(prev);
697 if ( (LastError != NO_ERROR
698 && (in.LowPart == INVALID_SET_FILE_POINTER
699 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
700 return Kumu::RESULT_READFAIL;
702 return Kumu::RESULT_OK;
707 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
711 if ( m_Handle == INVALID_HANDLE_VALUE )
712 return Kumu::RESULT_FILEOPEN;
715 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
716 in.QuadPart = (__int64)0;
717 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
718 HRESULT LastError = GetLastError();
719 ::SetErrorMode(prev);
721 if ( (LastError != NO_ERROR
722 && (in.LowPart == INVALID_SET_FILE_POINTER
723 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
724 return Kumu::RESULT_READFAIL;
726 *pos = (Kumu::fpos_t)in.QuadPart;
727 return Kumu::RESULT_OK;
732 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
735 Result_t result = Kumu::RESULT_OK;
739 if ( read_count == 0 )
740 read_count = &tmp_int;
744 if ( m_Handle == INVALID_HANDLE_VALUE )
745 return Kumu::RESULT_FILEOPEN;
747 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
748 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
749 result = Kumu::RESULT_READFAIL;
751 ::SetErrorMode(prev);
753 if ( tmp_count == 0 ) /* EOF */
754 result = Kumu::RESULT_ENDOFFILE;
756 if ( KM_SUCCESS(result) )
757 *read_count = tmp_count;
764 //------------------------------------------------------------------------------------------
769 Kumu::FileWriter::OpenWrite(const char* filename)
771 KM_TEST_NULL_STR_L(filename);
772 m_Filename = filename;
774 // suppress popup window on error
775 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
777 m_Handle = ::CreateFile(filename,
778 (GENERIC_WRITE|GENERIC_READ), // open for reading
779 FILE_SHARE_READ, // share for reading
781 CREATE_ALWAYS, // overwrite (beware!)
782 FILE_ATTRIBUTE_NORMAL, // normal file
783 NULL // no template file
786 ::SetErrorMode(prev);
788 if ( m_Handle == INVALID_HANDLE_VALUE )
789 return Kumu::RESULT_FILEOPEN;
791 m_IOVec = new h__iovec;
792 return Kumu::RESULT_OK;
797 Kumu::FileWriter::Writev(ui32_t* bytes_written)
799 assert( ! m_IOVec.empty() );
800 register h__iovec* iov = m_IOVec;
803 if ( bytes_written == 0 )
804 bytes_written = &tmp_int;
806 if ( m_Handle == INVALID_HANDLE_VALUE )
807 return Kumu::RESULT_STATE;
810 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
811 Result_t result = Kumu::RESULT_OK;
813 // AFAIK, there is no writev() equivalent in the win32 API
814 for ( register int i = 0; i < iov->m_Count; i++ )
816 ui32_t tmp_count = 0;
817 BOOL wr_result = ::WriteFile(m_Handle,
818 iov->m_iovec[i].iov_base,
819 iov->m_iovec[i].iov_len,
823 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
825 result = Kumu::RESULT_WRITEFAIL;
829 *bytes_written += tmp_count;
832 ::SetErrorMode(prev);
833 iov->m_Count = 0; // error nor not, all is lost
840 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
845 if ( bytes_written == 0 )
846 bytes_written = &tmp_int;
848 if ( m_Handle == INVALID_HANDLE_VALUE )
849 return Kumu::RESULT_STATE;
851 // suppress popup window on error
852 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
853 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
854 ::SetErrorMode(prev);
856 if ( result == 0 || *bytes_written != buf_len )
857 return Kumu::RESULT_WRITEFAIL;
859 return Kumu::RESULT_OK;
863 //------------------------------------------------------------------------------------------
868 Kumu::FileReader::OpenRead(const char* filename) const
870 KM_TEST_NULL_STR_L(filename);
871 const_cast<FileReader*>(this)->m_Filename = filename;
872 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
873 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
878 Kumu::FileReader::Close() const
880 if ( m_Handle == -1L )
881 return RESULT_FILEOPEN;
884 const_cast<FileReader*>(this)->m_Handle = -1L;
890 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
892 if ( m_Handle == -1L )
893 return RESULT_FILEOPEN;
895 if ( lseek(m_Handle, position, whence) == -1L )
896 return RESULT_BADSEEK;
903 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
907 if ( m_Handle == -1L )
908 return RESULT_FILEOPEN;
910 Kumu::fpos_t tmp_pos;
912 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
913 return RESULT_READFAIL;
921 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
927 if ( read_count == 0 )
928 read_count = &tmp_int;
932 if ( m_Handle == -1L )
933 return RESULT_FILEOPEN;
935 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
936 return RESULT_READFAIL;
938 *read_count = tmp_count;
939 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
943 //------------------------------------------------------------------------------------------
948 Kumu::FileWriter::OpenWrite(const char* filename)
950 KM_TEST_NULL_STR_L(filename);
951 m_Filename = filename;
952 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
954 if ( m_Handle == -1L )
956 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
957 return RESULT_FILEOPEN;
960 m_IOVec = new h__iovec;
966 Kumu::FileWriter::OpenModify(const char* filename)
968 KM_TEST_NULL_STR_L(filename);
969 m_Filename = filename;
970 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
972 if ( m_Handle == -1L )
974 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
975 return RESULT_FILEOPEN;
978 m_IOVec = new h__iovec;
984 Kumu::FileWriter::Writev(ui32_t* bytes_written)
986 assert( ! m_IOVec.empty() );
987 register h__iovec* iov = m_IOVec;
990 if ( bytes_written == 0 )
991 bytes_written = &tmp_int;
993 if ( m_Handle == -1L )
997 for ( int i = 0; i < iov->m_Count; i++ )
998 total_size += iov->m_iovec[i].iov_len;
1000 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1002 if ( write_size == -1L || write_size != total_size )
1003 return RESULT_WRITEFAIL;
1006 *bytes_written = write_size;
1012 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1014 KM_TEST_NULL_L(buf);
1017 if ( bytes_written == 0 )
1018 bytes_written = &tmp_int;
1020 if ( m_Handle == -1L )
1021 return RESULT_STATE;
1023 int write_size = write(m_Handle, buf, buf_len);
1025 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1026 return RESULT_WRITEFAIL;
1028 *bytes_written = write_size;
1035 //------------------------------------------------------------------------------------------
1040 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1043 ui32_t read_size = 0;
1047 KM_TEST_NULL_STR_L(filename);
1049 Result_t result = File.OpenRead(filename);
1051 if ( KM_SUCCESS(result) )
1053 fsize = File.Size();
1055 if ( fsize > (Kumu::fpos_t)max_size )
1057 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1058 return RESULT_ALLOC;
1063 DefaultLogSink().Error("%s: zero file size\n", filename);
1064 return RESULT_READFAIL;
1067 result = ReadBuf.Capacity((ui32_t)fsize);
1070 if ( KM_SUCCESS(result) )
1071 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1073 if ( KM_SUCCESS(result) )
1074 outString.assign((const char*)ReadBuf.RoData(), read_size);
1082 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1085 ui32_t write_count = 0;
1086 KM_TEST_NULL_STR_L(filename);
1088 Result_t result = File.OpenWrite(filename);
1090 if ( KM_SUCCESS(result) )
1091 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1096 //------------------------------------------------------------------------------------------
1101 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1104 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1105 Result_t result = Buffer.Capacity(file_size);
1107 if ( KM_SUCCESS(result) )
1109 ui32_t read_count = 0;
1112 result = Reader.OpenRead(Filename.c_str());
1114 if ( KM_SUCCESS(result) )
1115 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1117 if ( KM_SUCCESS(result) )
1119 assert(file_size == read_count);
1120 Buffer.Length(read_count);
1121 MemIOReader MemReader(&Buffer);
1122 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1131 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1134 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1136 if ( KM_SUCCESS(result) )
1138 ui32_t write_count = 0;
1140 MemIOWriter MemWriter(&Buffer);
1142 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1144 if ( KM_SUCCESS(result) )
1146 Buffer.Length(MemWriter.Length());
1147 result = Writer.OpenWrite(Filename.c_str());
1150 if ( KM_SUCCESS(result) )
1151 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1157 //------------------------------------------------------------------------------------------
1162 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1164 ui32_t file_size = FileSize(Filename);
1165 Result_t result = Buffer.Capacity(file_size);
1167 if ( KM_SUCCESS(result) )
1169 ui32_t read_count = 0;
1172 result = Reader.OpenRead(Filename.c_str());
1174 if ( KM_SUCCESS(result) )
1175 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1177 if ( KM_SUCCESS(result) )
1179 if ( file_size != read_count)
1180 return RESULT_READFAIL;
1182 Buffer.Length(read_count);
1191 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1193 ui32_t write_count = 0;
1196 Result_t result = Writer.OpenWrite(Filename.c_str());
1198 if ( KM_SUCCESS(result) )
1199 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1201 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1202 return RESULT_WRITEFAIL;
1207 //------------------------------------------------------------------------------------------
1211 // Win32 directory scanner
1216 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1221 Kumu::DirScanner::Open(const char* filename)
1223 KM_TEST_NULL_STR_L(filename);
1225 // we need to append a '*' to read the entire directory
1226 ui32_t fn_len = strlen(filename);
1227 char* tmp_file = (char*)malloc(fn_len + 8);
1229 if ( tmp_file == 0 )
1230 return RESULT_ALLOC;
1232 strcpy(tmp_file, filename);
1233 char* p = &tmp_file[fn_len] - 1;
1235 if ( *p != '/' && *p != '\\' )
1245 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1246 Result_t result = RESULT_OK;
1248 if ( m_Handle == -1 )
1249 result = RESULT_NOT_FOUND;
1258 Kumu::DirScanner::Close()
1260 if ( m_Handle == -1 )
1261 return RESULT_FILEOPEN;
1263 if ( _findclose((long)m_Handle) == -1 )
1271 // This sets filename param to the same per-instance buffer every time, so
1272 // the value will change on the next call
1274 Kumu::DirScanner::GetNext(char* filename)
1276 KM_TEST_NULL_L(filename);
1278 if ( m_Handle == -1 )
1279 return RESULT_FILEOPEN;
1281 if ( m_FileInfo.name[0] == '\0' )
1282 return RESULT_ENDOFFILE;
1284 strncpy(filename, m_FileInfo.name, MaxFilePath);
1285 Result_t result = RESULT_OK;
1287 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1289 m_FileInfo.name[0] = '\0';
1291 if ( errno != ENOENT )
1292 result = RESULT_FAIL;
1301 // POSIX directory scanner
1304 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1308 Kumu::DirScanner::Open(const char* filename)
1310 KM_TEST_NULL_STR_L(filename);
1312 Result_t result = RESULT_OK;
1314 if ( ( m_Handle = opendir(filename) ) == NULL )
1320 result = RESULT_NOTAFILE;
1322 result = RESULT_NO_PERM;
1325 result = RESULT_PARAM;
1328 result = RESULT_STATE;
1330 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", filename, strerror(errno));
1331 result = RESULT_FAIL;
1341 Kumu::DirScanner::Close()
1343 if ( m_Handle == NULL )
1344 return RESULT_FILEOPEN;
1346 if ( closedir(m_Handle) == -1 ) {
1351 return RESULT_STATE;
1353 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1365 Kumu::DirScanner::GetNext(char* filename)
1367 KM_TEST_NULL_L(filename);
1369 if ( m_Handle == NULL )
1370 return RESULT_FILEOPEN;
1372 struct dirent* entry;
1376 if ( ( entry = readdir(m_Handle)) == NULL )
1377 return RESULT_ENDOFFILE;
1382 strncpy(filename, entry->d_name, MaxFilePath);
1390 //------------------------------------------------------------------------------------------
1393 // Attention Windows users: make sure to use the proper separator character
1394 // with these functions.
1397 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1400 Kumu::CreateDirectoriesInPath(const std::string& Path)
1402 bool abs = PathIsAbsolute(Path);
1403 PathCompList_t PathComps, TmpPathComps;
1405 PathToComponents(Path, PathComps);
1407 while ( ! PathComps.empty() )
1409 TmpPathComps.push_back(PathComps.front());
1410 PathComps.pop_front();
1411 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1413 if ( ! PathIsDirectory(tmp_path) )
1416 if ( mkdir(tmp_path.c_str()) != 0 )
1418 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1421 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1422 tmp_path.c_str(), strerror(errno));
1423 return RESULT_DIR_CREATE;
1434 Kumu::DeleteFile(const std::string& filename)
1436 if ( unlink(filename.c_str()) == 0 )
1442 case ENOTDIR: return RESULT_NOTAFILE;
1447 case EPERM: return RESULT_NO_PERM;
1450 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1456 h__DeletePath(const std::string& pathname)
1458 fprintf(stderr, "h__DeletePath %s\n", pathname.c_str());
1459 Result_t result = RESULT_OK;
1461 if ( ! PathIsDirectory(pathname) )
1463 result = DeleteFile(pathname);
1469 char next_file[Kumu::MaxFilePath];
1470 result = TestDir.Open(pathname.c_str());
1472 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1474 if ( next_file[0] == '.' )
1476 if ( next_file[1] == 0 )
1477 continue; // don't delete 'this'
1479 if ( next_file[1] == '.' && next_file[2] == 0 )
1480 continue; // don't delete 'this' parent
1483 result = h__DeletePath(pathname + std::string("/") + next_file);
1487 if ( rmdir(pathname.c_str()) != 0 )
1493 result = RESULT_NOTAFILE;
1500 result = RESULT_NO_PERM;
1504 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1505 result = RESULT_FAIL;
1515 Kumu::DeletePath(const std::string& pathname)
1517 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1518 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1519 return h__DeletePath(c_pathname);
1523 //------------------------------------------------------------------------------------------
1528 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1531 ULARGE_INTEGER lTotalNumberOfBytes;
1532 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1534 BOOL fResult = ::GetDiskFreeSpaceEx(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1536 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1537 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1540 HRESULT LastError = ::GetLastError();
1542 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1547 if ( statfs(path.c_str(), &s) == 0 )
1549 if ( s.f_blocks < 1 )
1551 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1552 path.c_str(), s.f_blocks);
1556 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1557 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1564 case ENOTDIR: return RESULT_NOTAFILE;
1565 case EACCES: return RESULT_NO_PERM;
1568 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1575 // end KM_fileio.cpp