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, char separator, 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), 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 CList.push_back(cwd_buf);
354 CList.push_back(Path);
356 return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, separator, 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::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
435 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
437 PathList_t::const_iterator si;
438 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
440 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
442 if ( one_shot && ! FoundPaths.empty() )
451 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
452 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
454 char name_buf[MaxFilePath];
457 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
459 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
461 if ( name_buf[0] == '.' ) continue; // no hidden files
462 std::string tmp_path = SearchDir + separator + name_buf;
464 if ( PathIsDirectory(tmp_path.c_str()) )
465 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
467 else if ( Pattern.Match(name_buf) )
469 FoundPaths.push_back(SearchDir + separator + name_buf);
483 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
485 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
490 regerror(result, &m_regex, buf, 128);
491 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
496 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
497 m_regex = rhs.m_regex;
500 Kumu::PathMatchRegex::~PathMatchRegex() {
505 Kumu::PathMatchRegex::Match(const std::string& s) const {
506 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
512 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
514 std::string regex; // convert glob to regex
516 for ( const char* p = glob.c_str(); *p != 0; p++ )
520 case '.': regex += "\\."; break;
521 case '*': regex += ".*"; break;
522 case '?': regex += ".?"; break;
523 default: regex += *p;
528 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
533 regerror(result, &m_regex, buf, 128);
534 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
539 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
540 m_regex = rhs.m_regex;
543 Kumu::PathMatchGlob::~PathMatchGlob() {
548 Kumu::PathMatchGlob::Match(const std::string& s) const {
549 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
554 //------------------------------------------------------------------------------------------
555 // portable aspects of the file classes
557 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
560 class Kumu::FileWriter::h__iovec
564 struct iovec m_iovec[IOVecMaxEntries];
565 h__iovec() : m_Count(0) {}
572 Kumu::FileReader::Size() const
575 return FileSize(m_Filename.c_str());
579 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
581 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
582 return(info.st_size);
589 // these are declared here instead of in the header file
590 // because we have a mem_ptr that is managing a hidden class
591 Kumu::FileWriter::FileWriter() {}
592 Kumu::FileWriter::~FileWriter() {}
596 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
598 assert( ! m_IOVec.empty() );
599 register h__iovec* iov = m_IOVec;
602 if ( iov->m_Count >= IOVecMaxEntries )
604 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
606 return RESULT_WRITEFAIL;
609 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
610 iov->m_iovec[iov->m_Count].iov_len = buf_len;
618 //------------------------------------------------------------------------------------------
622 Kumu::FileReader::OpenRead(const char* filename) const
624 KM_TEST_NULL_STR_L(filename);
625 const_cast<FileReader*>(this)->m_Filename = filename;
627 // suppress popup window on error
628 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
630 const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
631 (GENERIC_READ), // open for reading
632 FILE_SHARE_READ, // share for reading
634 OPEN_EXISTING, // read
635 FILE_ATTRIBUTE_NORMAL, // normal file
636 NULL // no template file
639 ::SetErrorMode(prev);
641 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
642 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
647 Kumu::FileReader::Close() const
649 if ( m_Handle == INVALID_HANDLE_VALUE )
650 return Kumu::RESULT_FILEOPEN;
652 // suppress popup window on error
653 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
654 BOOL result = ::CloseHandle(m_Handle);
655 ::SetErrorMode(prev);
656 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
658 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
663 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
665 if ( m_Handle == INVALID_HANDLE_VALUE )
666 return Kumu::RESULT_STATE;
669 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
670 in.QuadPart = position;
671 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
672 HRESULT LastError = GetLastError();
673 ::SetErrorMode(prev);
675 if ( (LastError != NO_ERROR
676 && (in.LowPart == INVALID_SET_FILE_POINTER
677 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
678 return Kumu::RESULT_READFAIL;
680 return Kumu::RESULT_OK;
685 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
689 if ( m_Handle == INVALID_HANDLE_VALUE )
690 return Kumu::RESULT_FILEOPEN;
693 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
694 in.QuadPart = (__int64)0;
695 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
696 HRESULT LastError = GetLastError();
697 ::SetErrorMode(prev);
699 if ( (LastError != NO_ERROR
700 && (in.LowPart == INVALID_SET_FILE_POINTER
701 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
702 return Kumu::RESULT_READFAIL;
704 *pos = (Kumu::fpos_t)in.QuadPart;
705 return Kumu::RESULT_OK;
710 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
713 Result_t result = Kumu::RESULT_OK;
717 if ( read_count == 0 )
718 read_count = &tmp_int;
722 if ( m_Handle == INVALID_HANDLE_VALUE )
723 return Kumu::RESULT_FILEOPEN;
725 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
726 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
727 result = Kumu::RESULT_READFAIL;
729 ::SetErrorMode(prev);
731 if ( tmp_count == 0 ) /* EOF */
732 result = Kumu::RESULT_ENDOFFILE;
734 if ( KM_SUCCESS(result) )
735 *read_count = tmp_count;
742 //------------------------------------------------------------------------------------------
747 Kumu::FileWriter::OpenWrite(const char* filename)
749 KM_TEST_NULL_STR_L(filename);
750 m_Filename = filename;
752 // suppress popup window on error
753 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
755 m_Handle = ::CreateFile(filename,
756 (GENERIC_WRITE|GENERIC_READ), // open for reading
757 FILE_SHARE_READ, // share for reading
759 CREATE_ALWAYS, // overwrite (beware!)
760 FILE_ATTRIBUTE_NORMAL, // normal file
761 NULL // no template file
764 ::SetErrorMode(prev);
766 if ( m_Handle == INVALID_HANDLE_VALUE )
767 return Kumu::RESULT_FILEOPEN;
769 m_IOVec = new h__iovec;
770 return Kumu::RESULT_OK;
775 Kumu::FileWriter::Writev(ui32_t* bytes_written)
777 assert( ! m_IOVec.empty() );
778 register h__iovec* iov = m_IOVec;
781 if ( bytes_written == 0 )
782 bytes_written = &tmp_int;
784 if ( m_Handle == INVALID_HANDLE_VALUE )
785 return Kumu::RESULT_STATE;
788 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
789 Result_t result = Kumu::RESULT_OK;
791 // AFAIK, there is no writev() equivalent in the win32 API
792 for ( register int i = 0; i < iov->m_Count; i++ )
794 ui32_t tmp_count = 0;
795 BOOL wr_result = ::WriteFile(m_Handle,
796 iov->m_iovec[i].iov_base,
797 iov->m_iovec[i].iov_len,
801 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
803 result = Kumu::RESULT_WRITEFAIL;
807 *bytes_written += tmp_count;
810 ::SetErrorMode(prev);
811 iov->m_Count = 0; // error nor not, all is lost
818 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
823 if ( bytes_written == 0 )
824 bytes_written = &tmp_int;
826 if ( m_Handle == INVALID_HANDLE_VALUE )
827 return Kumu::RESULT_STATE;
829 // suppress popup window on error
830 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
831 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
832 ::SetErrorMode(prev);
834 if ( result == 0 || *bytes_written != buf_len )
835 return Kumu::RESULT_WRITEFAIL;
837 return Kumu::RESULT_OK;
841 //------------------------------------------------------------------------------------------
846 Kumu::FileReader::OpenRead(const char* filename) const
848 KM_TEST_NULL_STR_L(filename);
849 const_cast<FileReader*>(this)->m_Filename = filename;
850 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
851 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
856 Kumu::FileReader::Close() const
858 if ( m_Handle == -1L )
859 return RESULT_FILEOPEN;
862 const_cast<FileReader*>(this)->m_Handle = -1L;
868 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
870 if ( m_Handle == -1L )
871 return RESULT_FILEOPEN;
873 if ( lseek(m_Handle, position, whence) == -1L )
874 return RESULT_BADSEEK;
881 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
885 if ( m_Handle == -1L )
886 return RESULT_FILEOPEN;
888 Kumu::fpos_t tmp_pos;
890 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
891 return RESULT_READFAIL;
899 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
905 if ( read_count == 0 )
906 read_count = &tmp_int;
910 if ( m_Handle == -1L )
911 return RESULT_FILEOPEN;
913 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
914 return RESULT_READFAIL;
916 *read_count = tmp_count;
917 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
921 //------------------------------------------------------------------------------------------
926 Kumu::FileWriter::OpenWrite(const char* filename)
928 KM_TEST_NULL_STR_L(filename);
929 m_Filename = filename;
930 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
932 if ( m_Handle == -1L )
934 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
935 return RESULT_FILEOPEN;
938 m_IOVec = new h__iovec;
944 Kumu::FileWriter::OpenModify(const char* filename)
946 KM_TEST_NULL_STR_L(filename);
947 m_Filename = filename;
948 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
950 if ( m_Handle == -1L )
952 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
953 return RESULT_FILEOPEN;
956 m_IOVec = new h__iovec;
962 Kumu::FileWriter::Writev(ui32_t* bytes_written)
964 assert( ! m_IOVec.empty() );
965 register h__iovec* iov = m_IOVec;
968 if ( bytes_written == 0 )
969 bytes_written = &tmp_int;
971 if ( m_Handle == -1L )
975 for ( int i = 0; i < iov->m_Count; i++ )
976 total_size += iov->m_iovec[i].iov_len;
978 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
980 if ( write_size == -1L || write_size != total_size )
981 return RESULT_WRITEFAIL;
984 *bytes_written = write_size;
990 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
995 if ( bytes_written == 0 )
996 bytes_written = &tmp_int;
998 if ( m_Handle == -1L )
1001 int write_size = write(m_Handle, buf, buf_len);
1003 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1004 return RESULT_WRITEFAIL;
1006 *bytes_written = write_size;
1013 //------------------------------------------------------------------------------------------
1018 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1021 ui32_t read_size = 0;
1025 KM_TEST_NULL_STR_L(filename);
1027 Result_t result = File.OpenRead(filename);
1029 if ( KM_SUCCESS(result) )
1031 fsize = File.Size();
1033 if ( fsize > (Kumu::fpos_t)max_size )
1035 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1036 return RESULT_ALLOC;
1041 DefaultLogSink().Error("%s: zero file size\n", filename);
1042 return RESULT_READFAIL;
1045 result = ReadBuf.Capacity((ui32_t)fsize);
1048 if ( KM_SUCCESS(result) )
1049 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1051 if ( KM_SUCCESS(result) )
1052 outString.assign((const char*)ReadBuf.RoData(), read_size);
1060 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1063 ui32_t write_count = 0;
1064 KM_TEST_NULL_STR_L(filename);
1066 Result_t result = File.OpenWrite(filename);
1068 if ( KM_SUCCESS(result) )
1069 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1074 //------------------------------------------------------------------------------------------
1079 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1082 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1083 Result_t result = Buffer.Capacity(file_size);
1085 if ( KM_SUCCESS(result) )
1087 ui32_t read_count = 0;
1090 result = Reader.OpenRead(Filename.c_str());
1092 if ( KM_SUCCESS(result) )
1093 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1095 if ( KM_SUCCESS(result) )
1097 assert(file_size == read_count);
1098 Buffer.Length(read_count);
1099 MemIOReader MemReader(&Buffer);
1100 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1109 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1112 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1114 if ( KM_SUCCESS(result) )
1116 ui32_t write_count = 0;
1118 MemIOWriter MemWriter(&Buffer);
1120 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1122 if ( KM_SUCCESS(result) )
1124 Buffer.Length(MemWriter.Length());
1125 result = Writer.OpenWrite(Filename.c_str());
1128 if ( KM_SUCCESS(result) )
1129 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1135 //------------------------------------------------------------------------------------------
1140 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1142 ui32_t file_size = FileSize(Filename);
1143 Result_t result = Buffer.Capacity(file_size);
1145 if ( KM_SUCCESS(result) )
1147 ui32_t read_count = 0;
1150 result = Reader.OpenRead(Filename.c_str());
1152 if ( KM_SUCCESS(result) )
1153 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1155 if ( KM_SUCCESS(result) )
1157 if ( file_size != read_count)
1158 return RESULT_READFAIL;
1160 Buffer.Length(read_count);
1169 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1171 ui32_t write_count = 0;
1174 Result_t result = Writer.OpenWrite(Filename.c_str());
1176 if ( KM_SUCCESS(result) )
1177 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1179 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1180 return RESULT_WRITEFAIL;
1185 //------------------------------------------------------------------------------------------
1189 // Win32 directory scanner
1194 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1199 Kumu::DirScanner::Open(const char* filename)
1201 KM_TEST_NULL_STR_L(filename);
1203 // we need to append a '*' to read the entire directory
1204 ui32_t fn_len = strlen(filename);
1205 char* tmp_file = (char*)malloc(fn_len + 8);
1207 if ( tmp_file == 0 )
1208 return RESULT_ALLOC;
1210 strcpy(tmp_file, filename);
1211 char* p = &tmp_file[fn_len] - 1;
1213 if ( *p != '/' && *p != '\\' )
1223 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1224 Result_t result = RESULT_OK;
1226 if ( m_Handle == -1 )
1227 result = RESULT_NOT_FOUND;
1236 Kumu::DirScanner::Close()
1238 if ( m_Handle == -1 )
1239 return RESULT_FILEOPEN;
1241 if ( _findclose((long)m_Handle) == -1 )
1249 // This sets filename param to the same per-instance buffer every time, so
1250 // the value will change on the next call
1252 Kumu::DirScanner::GetNext(char* filename)
1254 KM_TEST_NULL_L(filename);
1256 if ( m_Handle == -1 )
1257 return RESULT_FILEOPEN;
1259 if ( m_FileInfo.name[0] == '\0' )
1260 return RESULT_ENDOFFILE;
1262 strncpy(filename, m_FileInfo.name, MaxFilePath);
1263 Result_t result = RESULT_OK;
1265 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1267 m_FileInfo.name[0] = '\0';
1269 if ( errno != ENOENT )
1270 result = RESULT_FAIL;
1279 // POSIX directory scanner
1282 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1286 Kumu::DirScanner::Open(const char* filename)
1288 KM_TEST_NULL_STR_L(filename);
1290 Result_t result = RESULT_OK;
1292 if ( ( m_Handle = opendir(filename) ) == NULL )
1298 result = RESULT_NOTAFILE;
1300 result = RESULT_NO_PERM;
1303 result = RESULT_PARAM;
1306 result = RESULT_STATE;
1308 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", filename, strerror(errno));
1309 result = RESULT_FAIL;
1319 Kumu::DirScanner::Close()
1321 if ( m_Handle == NULL )
1322 return RESULT_FILEOPEN;
1324 if ( closedir(m_Handle) == -1 ) {
1329 return RESULT_STATE;
1331 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1343 Kumu::DirScanner::GetNext(char* filename)
1345 KM_TEST_NULL_L(filename);
1347 if ( m_Handle == NULL )
1348 return RESULT_FILEOPEN;
1350 struct dirent* entry;
1354 if ( ( entry = readdir(m_Handle)) == NULL )
1355 return RESULT_ENDOFFILE;
1360 strncpy(filename, entry->d_name, MaxFilePath);
1368 //------------------------------------------------------------------------------------------
1371 // Attention Windows users: make sure to use the proper separator character
1372 // with these functions.
1375 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1378 Kumu::CreateDirectoriesInPath(const std::string& Path)
1380 bool abs = PathIsAbsolute(Path);
1382 PathCompList_t PathComps, TmpPathComps;
1384 PathToComponents(Path, PathComps);
1386 while ( ! PathComps.empty() )
1388 TmpPathComps.push_back(PathComps.front());
1389 PathComps.pop_front();
1390 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1392 if ( ! PathIsDirectory(tmp_path) )
1395 if ( mkdir(tmp_path.c_str()) != 0 )
1397 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1400 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1401 tmp_path.c_str(), strerror(errno));
1402 return RESULT_DIR_CREATE;
1413 Kumu::DeleteFile(const std::string& filename)
1415 if ( unlink(filename.c_str()) == 0 )
1421 case ENOTDIR: return RESULT_NOTAFILE;
1426 case EPERM: return RESULT_NO_PERM;
1429 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1435 h__DeletePath(const std::string& pathname)
1437 fprintf(stderr, "h__DeletePath %s\n", pathname.c_str());
1438 Result_t result = RESULT_OK;
1440 if ( ! PathIsDirectory(pathname) )
1442 result = DeleteFile(pathname);
1448 char next_file[Kumu::MaxFilePath];
1449 result = TestDir.Open(pathname.c_str());
1451 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1453 if ( next_file[0] == '.' )
1455 if ( next_file[1] == 0 )
1456 continue; // don't delete 'this'
1458 if ( next_file[1] == '.' && next_file[2] == 0 )
1459 continue; // don't delete 'this' parent
1462 result = h__DeletePath(pathname + std::string("/") + next_file);
1466 if ( rmdir(pathname.c_str()) != 0 )
1472 result = RESULT_NOTAFILE;
1479 result = RESULT_NO_PERM;
1483 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1484 result = RESULT_FAIL;
1494 Kumu::DeletePath(const std::string& pathname)
1496 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1497 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1498 return h__DeletePath(c_pathname);
1502 //------------------------------------------------------------------------------------------
1507 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1510 ULARGE_INTEGER lTotalNumberOfBytes;
1511 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1513 BOOL fResult = ::GetDiskFreeSpaceEx(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1515 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1516 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1519 HRESULT LastError = ::GetLastError();
1521 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1526 if ( statfs(path.c_str(), &s) == 0 )
1528 if ( s.f_blocks < 1 )
1530 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1531 path.c_str(), s.f_blocks);
1535 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1536 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1543 case ENOTDIR: return RESULT_NOTAFILE;
1544 case EACCES: return RESULT_NO_PERM;
1547 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1554 // end KM_fileio.cpp