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
1193 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1198 Kumu::DirScanner::Open(const char* filename)
1200 KM_TEST_NULL_STR_L(filename);
1202 // we need to append a '*' to read the entire directory
1203 ui32_t fn_len = strlen(filename);
1204 char* tmp_file = (char*)malloc(fn_len + 8);
1206 if ( tmp_file == 0 )
1207 return RESULT_ALLOC;
1209 strcpy(tmp_file, filename);
1210 char* p = &tmp_file[fn_len] - 1;
1212 if ( *p != '/' && *p != '\\' )
1222 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1223 Result_t result = RESULT_OK;
1225 if ( m_Handle == -1 )
1226 result = RESULT_NOT_FOUND;
1235 Kumu::DirScanner::Close()
1237 if ( m_Handle == -1 )
1238 return RESULT_FILEOPEN;
1240 if ( _findclose((long)m_Handle) == -1 )
1248 // This sets filename param to the same per-instance buffer every time, so
1249 // the value will change on the next call
1251 Kumu::DirScanner::GetNext(char* filename)
1253 KM_TEST_NULL_L(filename);
1255 if ( m_Handle == -1 )
1256 return RESULT_FILEOPEN;
1258 if ( m_FileInfo.name[0] == '\0' )
1259 return RESULT_ENDOFFILE;
1261 strncpy(filename, m_FileInfo.name, MaxFilePath);
1262 Result_t result = RESULT_OK;
1264 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1266 m_FileInfo.name[0] = '\0';
1268 if ( errno != ENOENT )
1269 result = RESULT_FAIL;
1278 // POSIX directory scanner
1281 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1285 Kumu::DirScanner::Open(const char* filename)
1287 KM_TEST_NULL_STR_L(filename);
1289 Result_t result = RESULT_OK;
1291 if ( ( m_Handle = opendir(filename) ) == NULL )
1297 result = RESULT_NOTAFILE;
1299 result = RESULT_NO_PERM;
1302 result = RESULT_PARAM;
1305 result = RESULT_STATE;
1307 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", filename, strerror(errno));
1308 result = RESULT_FAIL;
1318 Kumu::DirScanner::Close()
1320 if ( m_Handle == NULL )
1321 return RESULT_FILEOPEN;
1323 if ( closedir(m_Handle) == -1 ) {
1328 return RESULT_STATE;
1330 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1342 Kumu::DirScanner::GetNext(char* filename)
1344 KM_TEST_NULL_L(filename);
1346 if ( m_Handle == NULL )
1347 return RESULT_FILEOPEN;
1349 struct dirent* entry;
1353 if ( ( entry = readdir(m_Handle)) == NULL )
1354 return RESULT_ENDOFFILE;
1359 strncpy(filename, entry->d_name, MaxFilePath);
1367 //------------------------------------------------------------------------------------------
1370 // Attention Windows users: make sure to use the proper separator character
1371 // with these functions.
1374 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1377 Kumu::CreateDirectoriesInPath(const std::string& Path)
1379 bool abs = PathIsAbsolute(Path);
1381 PathCompList_t PathComps, TmpPathComps;
1383 PathToComponents(Path, PathComps);
1385 while ( ! PathComps.empty() )
1387 TmpPathComps.push_back(PathComps.front());
1388 PathComps.pop_front();
1389 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1391 if ( ! PathIsDirectory(tmp_path) )
1394 if ( mkdir(tmp_path.c_str()) != 0 )
1396 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1399 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1400 tmp_path.c_str(), strerror(errno));
1401 return RESULT_DIR_CREATE;
1412 Kumu::DeleteFile(const std::string& filename)
1414 if ( unlink(filename.c_str()) == 0 )
1420 case ENOTDIR: return RESULT_NOTAFILE;
1425 case EPERM: return RESULT_NO_PERM;
1428 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1434 h__DeletePath(const std::string& pathname)
1436 fprintf(stderr, "h__DeletePath %s\n", pathname.c_str());
1437 Result_t result = RESULT_OK;
1439 if ( ! PathIsDirectory(pathname) )
1441 result = DeleteFile(pathname);
1447 char next_file[Kumu::MaxFilePath];
1448 result = TestDir.Open(pathname.c_str());
1450 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1452 if ( next_file[0] == '.' )
1454 if ( next_file[1] == 0 )
1455 continue; // don't delete 'this'
1457 if ( next_file[1] == '.' && next_file[2] == 0 )
1458 continue; // don't delete 'this' parent
1461 result = h__DeletePath(pathname + std::string("/") + next_file);
1465 if ( rmdir(pathname.c_str()) != 0 )
1471 result = RESULT_NOTAFILE;
1478 result = RESULT_NO_PERM;
1482 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1483 result = RESULT_FAIL;
1493 Kumu::DeletePath(const std::string& pathname)
1495 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1496 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1497 return h__DeletePath(c_pathname);
1501 //------------------------------------------------------------------------------------------
1506 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1509 ULARGE_INTEGER lTotalNumberOfBytes;
\r
1510 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1512 BOOL fResult = ::GetDiskFreeSpaceEx(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1514 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1515 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1518 HRESULT LastError = ::GetLastError();
1520 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1525 if ( statfs(path.c_str(), &s) == 0 )
1527 if ( s.f_blocks < 1 )
1529 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1530 path.c_str(), s.f_blocks);
1534 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1535 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1542 case ENOTDIR: return RESULT_NOTAFILE;
1543 case EACCES: return RESULT_NO_PERM;
1546 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1553 // end KM_fileio.cpp