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/mount.h>
65 typedef struct stat fstat_t;
70 split(const std::string& str, char separator, std::list<std::string>& components)
72 const char* pstr = str.c_str();
73 const char* r = strchr(pstr, separator);
81 tmp_str.assign(pstr, (r - pstr));
82 components.push_back(tmp_str);
86 r = strchr(pstr, separator);
89 if( strlen(pstr) > 0 )
90 components.push_back(std::string(pstr));
96 do_stat(const char* path, fstat_t* stat_info)
98 KM_TEST_NULL_STR_L(path);
99 KM_TEST_NULL_L(stat_info);
101 Kumu::Result_t result = Kumu::RESULT_OK;
104 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
106 if ( _stati64(path, stat_info) == (__int64)-1 )
107 result = Kumu::RESULT_FILEOPEN;
109 ::SetErrorMode( prev );
111 if ( stat(path, stat_info) == -1L )
112 result = Kumu::RESULT_FILEOPEN;
114 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
115 result = Kumu::RESULT_FILEOPEN;
124 static Kumu::Result_t
125 do_fstat(FileHandle handle, fstat_t* stat_info)
127 KM_TEST_NULL_L(stat_info);
129 Kumu::Result_t result = Kumu::RESULT_OK;
131 if ( fstat(handle, stat_info) == -1L )
132 result = Kumu::RESULT_FILEOPEN;
134 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
135 result = Kumu::RESULT_FILEOPEN;
145 Kumu::PathExists(const std::string& pathname)
147 if ( pathname.empty() )
152 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
160 Kumu::PathIsFile(const std::string& pathname)
162 if ( pathname.empty() )
167 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
169 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
179 Kumu::PathIsDirectory(const std::string& pathname)
181 if ( pathname.empty() )
186 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
188 if ( info.st_mode & S_IFDIR )
197 Kumu::FileSize(const std::string& pathname)
199 if ( pathname.empty() )
204 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
206 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
207 return(info.st_size);
214 static PathCompList_t&
215 s_PathMakeCanonical(PathCompList_t& CList, char separator, bool is_absolute)
217 PathCompList_t::iterator ci, ri; // component and removal iterators
219 for ( ci = CList.begin(); ci != CList.end(); ci++ )
221 if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
226 else if ( *ci == ".." && ci != CList.begin() )
245 Kumu::PathMakeCanonical(const std::string& Path, char separator)
247 PathCompList_t CList;
248 bool is_absolute = PathIsAbsolute(Path, separator);
249 s_PathMakeCanonical(PathToComponents(Path, CList, separator), separator, is_absolute);
252 return ComponentsToAbsolutePath(CList, separator);
254 return ComponentsToPath(CList, separator);
259 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
261 return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
265 Kumu::PathCompList_t&
266 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
268 split(Path, separator, CList);
274 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
279 PathCompList_t::const_iterator ci = CList.begin();
280 std::string out_path = *ci;
282 for ( ci++; ci != CList.end(); ci++ )
283 out_path += separator + *ci;
290 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
292 std::string out_path;
295 out_path = separator;
298 PathCompList_t::const_iterator ci;
300 for ( ci = CList.begin(); ci != CList.end(); ci++ )
301 out_path += separator + *ci;
309 Kumu::PathHasComponents(const std::string& Path, char separator)
311 if ( strchr(Path.c_str(), separator) == 0 )
319 Kumu::PathIsAbsolute(const std::string& Path, char separator)
324 if ( Path[0] == separator)
332 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
336 std::string out_path;
337 out_path = separator;
341 if ( PathIsAbsolute(Path, separator) )
344 char cwd_buf [MaxFilePath];
345 if ( getcwd(cwd_buf, MaxFilePath) == 0 )
347 DefaultLogSink().Error("Error retrieving current working directory.");
351 PathCompList_t CList;
352 CList.push_back(cwd_buf);
353 CList.push_back(Path);
355 return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, separator, true), separator);
360 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
362 size_t pos = Path.find(Parent);
364 if ( pos == 0 ) // Parent found at offset 0
365 return Path.substr(Parent.size()+1);
372 Kumu::PathBasename(const std::string& Path, char separator)
374 PathCompList_t CList;
375 PathToComponents(Path, CList, separator);
385 Kumu::PathDirname(const std::string& Path, char separator)
387 PathCompList_t CList;
388 bool is_absolute = PathIsAbsolute(Path, separator);
389 PathToComponents(Path, CList, separator);
392 return is_absolute ? "/" : "";
397 return ComponentsToAbsolutePath(CList, separator);
399 return ComponentsToPath(CList, separator);
404 Kumu::PathGetExtension(const std::string& Path)
406 std::string Basename = PathBasename(Path);
407 const char* p = strrchr(Basename.c_str(), '.');
417 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
419 std::string Basename = PathBasename(Path);
420 const char* p = strrchr(Basename.c_str(), '.');
423 Basename = Basename.substr(0, p - Basename.c_str());
425 if ( Extension.empty() )
428 return Basename + "." + Extension;
433 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
434 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
436 PathList_t::const_iterator si;
437 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
439 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
441 if ( one_shot && ! FoundPaths.empty() )
450 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
451 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
453 char name_buf[MaxFilePath];
456 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
458 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
460 if ( name_buf[0] == '.' ) continue; // no hidden files
461 std::string tmp_path = SearchDir + separator + name_buf;
463 if ( PathIsDirectory(tmp_path.c_str()) )
464 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
466 else if ( Pattern.Match(name_buf) )
468 FoundPaths.push_back(SearchDir + separator + name_buf);
482 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
484 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
489 regerror(result, &m_regex, buf, 128);
490 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
495 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
496 m_regex = rhs.m_regex;
499 Kumu::PathMatchRegex::~PathMatchRegex() {
504 Kumu::PathMatchRegex::Match(const std::string& s) const {
505 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
511 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
513 std::string regex; // convert glob to regex
515 for ( const char* p = glob.c_str(); *p != 0; p++ )
519 case '.': regex += "\\."; break;
520 case '*': regex += ".*"; break;
521 case '?': regex += ".?"; break;
522 default: regex += *p;
527 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
532 regerror(result, &m_regex, buf, 128);
533 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
538 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
539 m_regex = rhs.m_regex;
542 Kumu::PathMatchGlob::~PathMatchGlob() {
547 Kumu::PathMatchGlob::Match(const std::string& s) const {
548 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
553 //------------------------------------------------------------------------------------------
554 // portable aspects of the file classes
556 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
559 class Kumu::FileWriter::h__iovec
563 struct iovec m_iovec[IOVecMaxEntries];
564 h__iovec() : m_Count(0) {}
571 Kumu::FileReader::Size() const
574 return FileSize(m_Filename.c_str());
578 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
580 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
581 return(info.st_size);
588 // these are declared here instead of in the header file
589 // because we have a mem_ptr that is managing a hidden class
590 Kumu::FileWriter::FileWriter() {}
591 Kumu::FileWriter::~FileWriter() {}
595 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
597 assert( ! m_IOVec.empty() );
598 register h__iovec* iov = m_IOVec;
601 if ( iov->m_Count >= IOVecMaxEntries )
603 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
605 return RESULT_WRITEFAIL;
608 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
609 iov->m_iovec[iov->m_Count].iov_len = buf_len;
617 //------------------------------------------------------------------------------------------
621 Kumu::FileReader::OpenRead(const char* filename) const
623 KM_TEST_NULL_STR_L(filename);
624 const_cast<FileReader*>(this)->m_Filename = filename;
626 // suppress popup window on error
627 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
629 const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
630 (GENERIC_READ), // open for reading
631 FILE_SHARE_READ, // share for reading
633 OPEN_EXISTING, // read
634 FILE_ATTRIBUTE_NORMAL, // normal file
635 NULL // no template file
638 ::SetErrorMode(prev);
640 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
641 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
646 Kumu::FileReader::Close() const
648 if ( m_Handle == INVALID_HANDLE_VALUE )
649 return Kumu::RESULT_FILEOPEN;
651 // suppress popup window on error
652 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
653 BOOL result = ::CloseHandle(m_Handle);
654 ::SetErrorMode(prev);
655 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
657 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
662 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
664 if ( m_Handle == INVALID_HANDLE_VALUE )
665 return Kumu::RESULT_STATE;
668 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
669 in.QuadPart = position;
670 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
671 HRESULT LastError = GetLastError();
672 ::SetErrorMode(prev);
674 if ( (LastError != NO_ERROR
675 && (in.LowPart == INVALID_SET_FILE_POINTER
676 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
677 return Kumu::RESULT_READFAIL;
679 return Kumu::RESULT_OK;
684 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
688 if ( m_Handle == INVALID_HANDLE_VALUE )
689 return Kumu::RESULT_FILEOPEN;
692 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
693 in.QuadPart = (__int64)0;
694 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
695 HRESULT LastError = GetLastError();
696 ::SetErrorMode(prev);
698 if ( (LastError != NO_ERROR
699 && (in.LowPart == INVALID_SET_FILE_POINTER
700 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
701 return Kumu::RESULT_READFAIL;
703 *pos = (Kumu::fpos_t)in.QuadPart;
704 return Kumu::RESULT_OK;
709 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
712 Result_t result = Kumu::RESULT_OK;
716 if ( read_count == 0 )
717 read_count = &tmp_int;
721 if ( m_Handle == INVALID_HANDLE_VALUE )
722 return Kumu::RESULT_FILEOPEN;
724 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
725 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
726 result = Kumu::RESULT_READFAIL;
728 ::SetErrorMode(prev);
730 if ( tmp_count == 0 ) /* EOF */
731 result = Kumu::RESULT_ENDOFFILE;
733 if ( KM_SUCCESS(result) )
734 *read_count = tmp_count;
741 //------------------------------------------------------------------------------------------
746 Kumu::FileWriter::OpenWrite(const char* filename)
748 KM_TEST_NULL_STR_L(filename);
749 m_Filename = filename;
751 // suppress popup window on error
752 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
754 m_Handle = ::CreateFile(filename,
755 (GENERIC_WRITE|GENERIC_READ), // open for reading
756 FILE_SHARE_READ, // share for reading
758 CREATE_ALWAYS, // overwrite (beware!)
759 FILE_ATTRIBUTE_NORMAL, // normal file
760 NULL // no template file
763 ::SetErrorMode(prev);
765 if ( m_Handle == INVALID_HANDLE_VALUE )
766 return Kumu::RESULT_FILEOPEN;
768 m_IOVec = new h__iovec;
769 return Kumu::RESULT_OK;
774 Kumu::FileWriter::Writev(ui32_t* bytes_written)
776 assert( ! m_IOVec.empty() );
777 register h__iovec* iov = m_IOVec;
780 if ( bytes_written == 0 )
781 bytes_written = &tmp_int;
783 if ( m_Handle == INVALID_HANDLE_VALUE )
784 return Kumu::RESULT_STATE;
787 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
788 Result_t result = Kumu::RESULT_OK;
790 // AFAIK, there is no writev() equivalent in the win32 API
791 for ( register int i = 0; i < iov->m_Count; i++ )
793 ui32_t tmp_count = 0;
794 BOOL wr_result = ::WriteFile(m_Handle,
795 iov->m_iovec[i].iov_base,
796 iov->m_iovec[i].iov_len,
800 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
802 result = Kumu::RESULT_WRITEFAIL;
806 *bytes_written += tmp_count;
809 ::SetErrorMode(prev);
810 iov->m_Count = 0; // error nor not, all is lost
817 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
822 if ( bytes_written == 0 )
823 bytes_written = &tmp_int;
825 if ( m_Handle == INVALID_HANDLE_VALUE )
826 return Kumu::RESULT_STATE;
828 // suppress popup window on error
829 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
830 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
831 ::SetErrorMode(prev);
833 if ( result == 0 || *bytes_written != buf_len )
834 return Kumu::RESULT_WRITEFAIL;
836 return Kumu::RESULT_OK;
840 //------------------------------------------------------------------------------------------
845 Kumu::FileReader::OpenRead(const char* filename) const
847 KM_TEST_NULL_STR_L(filename);
848 const_cast<FileReader*>(this)->m_Filename = filename;
849 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
850 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
855 Kumu::FileReader::Close() const
857 if ( m_Handle == -1L )
858 return RESULT_FILEOPEN;
861 const_cast<FileReader*>(this)->m_Handle = -1L;
867 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
869 if ( m_Handle == -1L )
870 return RESULT_FILEOPEN;
872 if ( lseek(m_Handle, position, whence) == -1L )
873 return RESULT_BADSEEK;
880 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
884 if ( m_Handle == -1L )
885 return RESULT_FILEOPEN;
887 Kumu::fpos_t tmp_pos;
889 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
890 return RESULT_READFAIL;
898 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
904 if ( read_count == 0 )
905 read_count = &tmp_int;
909 if ( m_Handle == -1L )
910 return RESULT_FILEOPEN;
912 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
913 return RESULT_READFAIL;
915 *read_count = tmp_count;
916 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
920 //------------------------------------------------------------------------------------------
925 Kumu::FileWriter::OpenWrite(const char* filename)
927 KM_TEST_NULL_STR_L(filename);
928 m_Filename = filename;
929 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
931 if ( m_Handle == -1L )
933 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
934 return RESULT_FILEOPEN;
937 m_IOVec = new h__iovec;
943 Kumu::FileWriter::OpenModify(const char* filename)
945 KM_TEST_NULL_STR_L(filename);
946 m_Filename = filename;
947 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
949 if ( m_Handle == -1L )
951 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
952 return RESULT_FILEOPEN;
955 m_IOVec = new h__iovec;
961 Kumu::FileWriter::Writev(ui32_t* bytes_written)
963 assert( ! m_IOVec.empty() );
964 register h__iovec* iov = m_IOVec;
967 if ( bytes_written == 0 )
968 bytes_written = &tmp_int;
970 if ( m_Handle == -1L )
974 for ( int i = 0; i < iov->m_Count; i++ )
975 total_size += iov->m_iovec[i].iov_len;
977 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
979 if ( write_size == -1L || write_size != total_size )
980 return RESULT_WRITEFAIL;
983 *bytes_written = write_size;
989 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
994 if ( bytes_written == 0 )
995 bytes_written = &tmp_int;
997 if ( m_Handle == -1L )
1000 int write_size = write(m_Handle, buf, buf_len);
1002 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1003 return RESULT_WRITEFAIL;
1005 *bytes_written = write_size;
1012 //------------------------------------------------------------------------------------------
1017 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1020 ui32_t read_size = 0;
1024 KM_TEST_NULL_STR_L(filename);
1026 Result_t result = File.OpenRead(filename);
1028 if ( KM_SUCCESS(result) )
1030 fsize = File.Size();
1032 if ( fsize > (Kumu::fpos_t)max_size )
1034 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1035 return RESULT_ALLOC;
1040 DefaultLogSink().Error("%s: zero file size\n", filename);
1041 return RESULT_READFAIL;
1044 result = ReadBuf.Capacity((ui32_t)fsize);
1047 if ( KM_SUCCESS(result) )
1048 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1050 if ( KM_SUCCESS(result) )
1051 outString.assign((const char*)ReadBuf.RoData(), read_size);
1059 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1062 ui32_t write_count = 0;
1063 KM_TEST_NULL_STR_L(filename);
1065 Result_t result = File.OpenWrite(filename);
1067 if ( KM_SUCCESS(result) )
1068 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1073 //------------------------------------------------------------------------------------------
1078 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1081 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1082 Result_t result = Buffer.Capacity(file_size);
1084 if ( KM_SUCCESS(result) )
1086 ui32_t read_count = 0;
1089 result = Reader.OpenRead(Filename.c_str());
1091 if ( KM_SUCCESS(result) )
1092 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1094 if ( KM_SUCCESS(result) )
1096 assert(file_size == read_count);
1097 Buffer.Length(read_count);
1098 MemIOReader MemReader(&Buffer);
1099 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1108 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1111 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1113 if ( KM_SUCCESS(result) )
1115 ui32_t write_count = 0;
1117 MemIOWriter MemWriter(&Buffer);
1119 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1121 if ( KM_SUCCESS(result) )
1123 Buffer.Length(MemWriter.Length());
1124 result = Writer.OpenWrite(Filename.c_str());
1127 if ( KM_SUCCESS(result) )
1128 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1134 //------------------------------------------------------------------------------------------
1139 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1141 ui32_t file_size = FileSize(Filename);
1142 Result_t result = Buffer.Capacity(file_size);
1144 if ( KM_SUCCESS(result) )
1146 ui32_t read_count = 0;
1149 result = Reader.OpenRead(Filename.c_str());
1151 if ( KM_SUCCESS(result) )
1152 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1154 if ( KM_SUCCESS(result) )
1156 if ( file_size != read_count)
1157 return RESULT_READFAIL;
1159 Buffer.Length(read_count);
1168 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1170 ui32_t write_count = 0;
1173 Result_t result = Writer.OpenWrite(Filename.c_str());
1175 if ( KM_SUCCESS(result) )
1176 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1178 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1179 return RESULT_WRITEFAIL;
1184 //------------------------------------------------------------------------------------------
1188 // Win32 directory scanner
1195 Kumu::DirScanner::Open(const char* filename)
1197 KM_TEST_NULL_STR_L(filename);
1199 // we need to append a '*' to read the entire directory
1200 ui32_t fn_len = strlen(filename);
1201 char* tmp_file = (char*)malloc(fn_len + 8);
1203 if ( tmp_file == 0 )
1204 return RESULT_ALLOC;
1206 strcpy(tmp_file, filename);
1207 char* p = &tmp_file[fn_len] - 1;
1209 if ( *p != '/' && *p != '\\' )
1219 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1220 Result_t result = RESULT_OK;
1222 if ( m_Handle == -1 )
1223 result = RESULT_NOT_FOUND;
1232 Kumu::DirScanner::Close()
1234 if ( m_Handle == -1 )
1235 return RESULT_FILEOPEN;
1237 if ( _findclose((long)m_Handle) == -1 )
1245 // This sets filename param to the same per-instance buffer every time, so
1246 // the value will change on the next call
1248 Kumu::DirScanner::GetNext(char* filename)
1250 KM_TEST_NULL_L(filename);
1252 if ( m_Handle == -1 )
1253 return RESULT_FILEOPEN;
1255 if ( m_FileInfo.name[0] == '\0' )
1256 return RESULT_ENDOFFILE;
1258 strncpy(filename, m_FileInfo.name, MaxFilePath);
1259 Result_t result = RESULT_OK;
1261 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1263 m_FileInfo.name[0] = '\0';
1265 if ( errno != ENOENT )
1266 result = RESULT_FAIL;
1275 // POSIX directory scanner
1279 Kumu::DirScanner::Open(const char* filename)
1281 KM_TEST_NULL_STR_L(filename);
1283 Result_t result = RESULT_OK;
1285 if ( ( m_Handle = opendir(filename) ) == NULL )
1287 if ( errno == ENOENT )
1288 result = RESULT_ENDOFFILE;
1291 result = RESULT_FAIL;
1300 Kumu::DirScanner::Close()
1302 if ( m_Handle == NULL )
1303 return RESULT_FILEOPEN;
1305 if ( closedir(m_Handle) == -1 )
1315 Kumu::DirScanner::GetNext(char* filename)
1317 KM_TEST_NULL_L(filename);
1319 if ( m_Handle == NULL )
1320 return RESULT_FILEOPEN;
1322 struct dirent* entry;
1326 if ( ( entry = readdir(m_Handle)) == NULL )
1327 return RESULT_ENDOFFILE;
1332 strncpy(filename, entry->d_name, MaxFilePath);
1340 //------------------------------------------------------------------------------------------
1342 // note: when moving to KM_fileio, don't forget to write the Win32 versions
1343 // note: add error messages and remove RESULT_FAIL form DirScanner
1348 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1351 Kumu::CreateDirectoriesInPath(const std::string& Path)
1353 bool abs = PathIsAbsolute(Path);
1355 PathCompList_t PathComps, TmpPathComps;
1357 PathToComponents(Path, PathComps);
1359 while ( ! PathComps.empty() )
1361 TmpPathComps.push_back(PathComps.front());
1362 PathComps.pop_front();
1363 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1365 if ( ! PathIsDirectory(tmp_path) )
1367 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1369 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1370 tmp_path.c_str(), strerror(errno));
1371 return RESULT_DIR_CREATE;
1386 Kumu::DeleteFile(const std::string& filename)
1388 if ( unlink(filename.c_str()) == 0 )
1394 case ENOTDIR: return RESULT_NOTAFILE;
1399 case EPERM: return RESULT_NO_PERM;
1402 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1408 h__DeletePath(const std::string& pathname)
1410 fprintf(stderr, "h__DeletePath %s\n", pathname.c_str());
1411 Result_t result = RESULT_OK;
1413 if ( ! PathIsDirectory(pathname) )
1415 result = DeleteFile(pathname);
1421 char next_file[Kumu::MaxFilePath];
1422 result = TestDir.Open(pathname.c_str());
1424 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1426 if ( next_file[0] == '.' )
1428 if ( next_file[1] == 0 )
1429 continue; // don't delete 'this'
1431 if ( next_file[1] == '.' && next_file[2] == 0 )
1432 continue; // don't delete 'this' parent
1435 result = h__DeletePath(pathname + std::string("/") + next_file);
1439 if ( rmdir(pathname.c_str()) != 0 )
1445 result = RESULT_NOTAFILE;
1452 result = RESULT_NO_PERM;
1456 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1457 result = RESULT_FAIL;
1467 Kumu::DeletePath(const std::string& pathname)
1469 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1470 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1471 return h__DeletePath(c_pathname);
1476 //------------------------------------------------------------------------------------------
1485 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1489 if ( statfs(path.c_str(), &s) == 0 )
1491 if ( s.f_blocks < 1 )
1493 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1494 path.c_str(), s.f_blocks);
1498 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1499 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1506 case ENOTDIR: return RESULT_NOTAFILE;
1507 case EACCES: return RESULT_NO_PERM;
1510 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1517 // end KM_fileio.cpp