2 Copyright (c) 2004-2011, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file KM_fileio.cpp
28 \version $Id: KM_fileio.cpp,v 1.31 2011/03/08 19:03:47 jhurst Exp $
29 \brief portable file i/o
32 #include <KM_fileio.h>
43 #define _getcwd getcwd
44 #define _unlink unlink
51 typedef struct _stati64 fstat_t;
54 // win32 has WriteFileGather() and ReadFileScatter() but they
55 // demand page alignment and page sizing, making them unsuitable
56 // for use with arbitrary buffer sizes.
58 char* iov_base; // stupid iovec uses char*
62 # if defined(__linux__)
63 # include <sys/statfs.h>
65 # include <sys/param.h>
66 # include <sys/mount.h>
71 typedef struct stat fstat_t;
76 split(const std::string& str, char separator, std::list<std::string>& components)
78 const char* pstr = str.c_str();
79 const char* r = strchr(pstr, separator);
87 tmp_str.assign(pstr, (r - pstr));
88 components.push_back(tmp_str);
92 r = strchr(pstr, separator);
95 if( strlen(pstr) > 0 )
96 components.push_back(std::string(pstr));
101 static Kumu::Result_t
102 do_stat(const char* path, fstat_t* stat_info)
104 KM_TEST_NULL_STR_L(path);
105 KM_TEST_NULL_L(stat_info);
107 Kumu::Result_t result = Kumu::RESULT_OK;
110 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
112 if ( _stati64(path, stat_info) == (__int64)-1 )
113 result = Kumu::RESULT_FILEOPEN;
115 ::SetErrorMode( prev );
117 if ( stat(path, stat_info) == -1L )
118 result = Kumu::RESULT_FILEOPEN;
120 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
121 result = Kumu::RESULT_FILEOPEN;
130 static Kumu::Result_t
131 do_fstat(FileHandle handle, fstat_t* stat_info)
133 KM_TEST_NULL_L(stat_info);
135 Kumu::Result_t result = Kumu::RESULT_OK;
137 if ( fstat(handle, stat_info) == -1L )
138 result = Kumu::RESULT_FILEOPEN;
140 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
141 result = Kumu::RESULT_FILEOPEN;
151 Kumu::PathExists(const std::string& pathname)
153 if ( pathname.empty() )
158 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
166 Kumu::PathIsFile(const std::string& pathname)
168 if ( pathname.empty() )
173 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
175 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
185 Kumu::PathIsDirectory(const std::string& pathname)
187 if ( pathname.empty() )
192 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
194 if ( info.st_mode & S_IFDIR )
203 Kumu::FileSize(const std::string& pathname)
205 if ( pathname.empty() )
210 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
212 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
213 return(info.st_size);
220 static PathCompList_t&
221 s_PathMakeCanonical(PathCompList_t& CList, bool is_absolute)
223 PathCompList_t::iterator ci, ri; // component and removal iterators
225 for ( ci = CList.begin(); ci != CList.end(); ci++ )
227 if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
232 else if ( *ci == ".." && ci != CList.begin() )
251 Kumu::PathMakeCanonical(const std::string& Path, char separator)
253 PathCompList_t CList;
254 bool is_absolute = PathIsAbsolute(Path, separator);
255 s_PathMakeCanonical(PathToComponents(Path, CList, separator), is_absolute);
258 return ComponentsToAbsolutePath(CList, separator);
260 return ComponentsToPath(CList, separator);
265 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
267 return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
271 Kumu::PathCompList_t&
272 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
274 split(Path, separator, CList);
280 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
285 PathCompList_t::const_iterator ci = CList.begin();
286 std::string out_path = *ci;
288 for ( ci++; ci != CList.end(); ci++ )
289 out_path += separator + *ci;
296 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
298 std::string out_path;
301 out_path = separator;
304 PathCompList_t::const_iterator ci;
306 for ( ci = CList.begin(); ci != CList.end(); ci++ )
307 out_path += separator + *ci;
315 Kumu::PathHasComponents(const std::string& Path, char separator)
317 if ( strchr(Path.c_str(), separator) == 0 )
325 Kumu::PathIsAbsolute(const std::string& Path, char separator)
330 if ( Path[0] == separator)
338 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
342 std::string out_path;
343 out_path = separator;
347 if ( PathIsAbsolute(Path, separator) )
350 char cwd_buf [MaxFilePath];
351 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
353 DefaultLogSink().Error("Error retrieving current working directory.");
357 PathCompList_t CList;
358 PathToComponents(cwd_buf, CList);
359 CList.push_back(Path);
361 return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, true), separator);
366 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
368 size_t pos = Path.find(Parent);
370 if ( pos == 0 ) // Parent found at offset 0
371 return Path.substr(Parent.size()+1);
378 Kumu::PathBasename(const std::string& Path, char separator)
380 PathCompList_t CList;
381 PathToComponents(Path, CList, separator);
391 Kumu::PathDirname(const std::string& Path, char separator)
393 PathCompList_t CList;
394 bool is_absolute = PathIsAbsolute(Path, separator);
395 PathToComponents(Path, CList, separator);
398 return is_absolute ? "/" : "";
403 return ComponentsToAbsolutePath(CList, separator);
405 return ComponentsToPath(CList, separator);
410 Kumu::PathGetExtension(const std::string& Path)
412 std::string Basename = PathBasename(Path);
413 const char* p = strrchr(Basename.c_str(), '.');
423 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
425 std::string Basename = PathBasename(Path);
426 const char* p = strrchr(Basename.c_str(), '.');
429 Basename = Basename.substr(0, p - Basename.c_str());
431 if ( Extension.empty() )
434 return Basename + "." + Extension;
439 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
441 return Path1 + separator + Path2;
446 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
448 return Path1 + separator + Path2 + separator + Path3;
453 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
454 const std::string& Path3, const std::string& Path4, char separator)
456 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
461 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
462 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
464 PathList_t::const_iterator si;
465 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
467 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
469 if ( one_shot && ! FoundPaths.empty() )
478 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
479 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
481 char name_buf[MaxFilePath];
484 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
486 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
488 if ( name_buf[0] == '.' ) continue; // no hidden files
489 std::string tmp_path = SearchDir + separator + name_buf;
491 if ( PathIsDirectory(tmp_path.c_str()) )
492 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
494 else if ( Pattern.Match(name_buf) )
496 FoundPaths.push_back(SearchDir + separator + name_buf);
510 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
512 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
517 regerror(result, &m_regex, buf, 128);
518 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
523 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
524 m_regex = rhs.m_regex;
527 Kumu::PathMatchRegex::~PathMatchRegex() {
532 Kumu::PathMatchRegex::Match(const std::string& s) const {
533 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
539 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
541 std::string regex; // convert glob to regex
543 for ( const char* p = glob.c_str(); *p != 0; p++ )
547 case '.': regex += "\\."; break;
548 case '*': regex += ".*"; break;
549 case '?': regex += ".?"; break;
550 default: regex += *p;
555 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
560 regerror(result, &m_regex, buf, 128);
561 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
566 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
567 m_regex = rhs.m_regex;
570 Kumu::PathMatchGlob::~PathMatchGlob() {
575 Kumu::PathMatchGlob::Match(const std::string& s) const {
576 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
581 //------------------------------------------------------------------------------------------
582 // portable aspects of the file classes
584 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
587 class Kumu::FileWriter::h__iovec
591 struct iovec m_iovec[IOVecMaxEntries];
592 h__iovec() : m_Count(0) {}
599 Kumu::FileReader::Size() const
602 return FileSize(m_Filename.c_str());
606 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
608 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
609 return(info.st_size);
616 // these are declared here instead of in the header file
617 // because we have a mem_ptr that is managing a hidden class
618 Kumu::FileWriter::FileWriter()
622 Kumu::FileWriter::~FileWriter() {}
626 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
628 assert( ! m_IOVec.empty() );
629 register h__iovec* iov = m_IOVec;
632 if ( iov->m_Count >= IOVecMaxEntries )
634 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
636 return RESULT_WRITEFAIL;
639 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
640 iov->m_iovec[iov->m_Count].iov_len = buf_len;
647 Kumu::FileWriter::StartHashing()
650 MD5_Init (&m_MD5Context);
654 Kumu::FileWriter::MaybeHash(void const * data, int size)
657 MD5_Update (&m_MD5Context, data, size);
662 Kumu::FileWriter::StopHashing()
666 unsigned char digest[MD5_DIGEST_LENGTH];
667 MD5_Final (digest, &m_MD5Context);
670 for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
671 s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
679 //------------------------------------------------------------------------------------------
683 Kumu::FileReader::OpenRead(const char* filename) const
685 KM_TEST_NULL_STR_L(filename);
686 const_cast<FileReader*>(this)->m_Filename = filename;
688 // suppress popup window on error
689 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
691 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename,
692 (GENERIC_READ), // open for reading
693 FILE_SHARE_READ, // share for reading
695 OPEN_EXISTING, // read
696 FILE_ATTRIBUTE_NORMAL, // normal file
697 NULL // no template file
700 ::SetErrorMode(prev);
702 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
703 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
708 Kumu::FileReader::Close() const
710 if ( m_Handle == INVALID_HANDLE_VALUE )
711 return Kumu::RESULT_FILEOPEN;
713 // suppress popup window on error
714 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
715 BOOL result = ::CloseHandle(m_Handle);
716 ::SetErrorMode(prev);
717 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
719 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
724 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
726 if ( m_Handle == INVALID_HANDLE_VALUE )
727 return Kumu::RESULT_STATE;
730 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
731 in.QuadPart = position;
732 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
733 HRESULT LastError = GetLastError();
734 ::SetErrorMode(prev);
736 if ( (LastError != NO_ERROR
737 && (in.LowPart == INVALID_SET_FILE_POINTER
738 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
739 return Kumu::RESULT_READFAIL;
741 return Kumu::RESULT_OK;
746 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
750 if ( m_Handle == INVALID_HANDLE_VALUE )
751 return Kumu::RESULT_FILEOPEN;
754 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
755 in.QuadPart = (__int64)0;
756 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
757 HRESULT LastError = GetLastError();
758 ::SetErrorMode(prev);
760 if ( (LastError != NO_ERROR
761 && (in.LowPart == INVALID_SET_FILE_POINTER
762 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
763 return Kumu::RESULT_READFAIL;
765 *pos = (Kumu::fpos_t)in.QuadPart;
766 return Kumu::RESULT_OK;
771 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
774 Result_t result = Kumu::RESULT_OK;
778 if ( read_count == 0 )
779 read_count = &tmp_int;
783 if ( m_Handle == INVALID_HANDLE_VALUE )
784 return Kumu::RESULT_FILEOPEN;
786 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
787 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
788 result = Kumu::RESULT_READFAIL;
790 ::SetErrorMode(prev);
792 if ( tmp_count == 0 ) /* EOF */
793 result = Kumu::RESULT_ENDOFFILE;
795 if ( KM_SUCCESS(result) )
796 *read_count = tmp_count;
803 //------------------------------------------------------------------------------------------
808 Kumu::FileWriter::OpenWrite(const char* filename)
810 KM_TEST_NULL_STR_L(filename);
811 m_Filename = filename;
813 // suppress popup window on error
814 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
816 m_Handle = ::CreateFileA(filename,
817 (GENERIC_WRITE|GENERIC_READ), // open for reading
818 FILE_SHARE_READ, // share for reading
820 CREATE_ALWAYS, // overwrite (beware!)
821 FILE_ATTRIBUTE_NORMAL, // normal file
822 NULL // no template file
825 ::SetErrorMode(prev);
827 if ( m_Handle == INVALID_HANDLE_VALUE )
828 return Kumu::RESULT_FILEOPEN;
830 m_IOVec = new h__iovec;
831 return Kumu::RESULT_OK;
836 Kumu::FileWriter::Writev(ui32_t* bytes_written)
838 assert( ! m_IOVec.empty() );
839 register h__iovec* iov = m_IOVec;
842 if ( bytes_written == 0 )
843 bytes_written = &tmp_int;
845 if ( m_Handle == INVALID_HANDLE_VALUE )
846 return Kumu::RESULT_STATE;
849 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
850 Result_t result = Kumu::RESULT_OK;
852 // AFAIK, there is no writev() equivalent in the win32 API
853 for ( register int i = 0; i < iov->m_Count; i++ )
855 ui32_t tmp_count = 0;
856 BOOL wr_result = ::WriteFile(m_Handle,
857 iov->m_iovec[i].iov_base,
858 iov->m_iovec[i].iov_len,
862 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
864 result = Kumu::RESULT_WRITEFAIL;
868 MaybeHash (iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
869 *bytes_written += tmp_count;
872 ::SetErrorMode(prev);
873 iov->m_Count = 0; // error nor not, all is lost
880 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
885 if ( bytes_written == 0 )
886 bytes_written = &tmp_int;
888 if ( m_Handle == INVALID_HANDLE_VALUE )
889 return Kumu::RESULT_STATE;
891 // suppress popup window on error
892 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
893 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
894 ::SetErrorMode(prev);
896 if ( result == 0 || *bytes_written != buf_len )
897 return Kumu::RESULT_WRITEFAIL;
899 MaybeHash (buf, buf_len);
901 return Kumu::RESULT_OK;
905 //------------------------------------------------------------------------------------------
910 Kumu::FileReader::OpenRead(const char* filename) const
912 KM_TEST_NULL_STR_L(filename);
913 const_cast<FileReader*>(this)->m_Filename = filename;
914 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
915 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
920 Kumu::FileReader::Close() const
922 if ( m_Handle == -1L )
923 return RESULT_FILEOPEN;
926 const_cast<FileReader*>(this)->m_Handle = -1L;
932 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
934 if ( m_Handle == -1L )
935 return RESULT_FILEOPEN;
937 if ( lseek(m_Handle, position, whence) == -1L )
938 return RESULT_BADSEEK;
945 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
949 if ( m_Handle == -1L )
950 return RESULT_FILEOPEN;
952 Kumu::fpos_t tmp_pos;
954 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
955 return RESULT_READFAIL;
963 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
969 if ( read_count == 0 )
970 read_count = &tmp_int;
974 if ( m_Handle == -1L )
975 return RESULT_FILEOPEN;
977 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
978 return RESULT_READFAIL;
980 *read_count = tmp_count;
981 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
985 //------------------------------------------------------------------------------------------
990 Kumu::FileWriter::OpenWrite(const char* filename)
992 KM_TEST_NULL_STR_L(filename);
993 m_Filename = filename;
994 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
996 if ( m_Handle == -1L )
998 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
999 return RESULT_FILEOPEN;
1002 m_IOVec = new h__iovec;
1008 Kumu::FileWriter::OpenModify(const char* filename)
1010 KM_TEST_NULL_STR_L(filename);
1011 m_Filename = filename;
1012 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
1014 if ( m_Handle == -1L )
1016 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
1017 return RESULT_FILEOPEN;
1020 m_IOVec = new h__iovec;
1026 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1028 assert( ! m_IOVec.empty() );
1029 register h__iovec* iov = m_IOVec;
1032 if ( bytes_written == 0 )
1033 bytes_written = &tmp_int;
1035 if ( m_Handle == -1L )
1036 return RESULT_STATE;
1039 for ( int i = 0; i < iov->m_Count; i++ )
1040 total_size += iov->m_iovec[i].iov_len;
1042 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1044 if ( write_size == -1L || write_size != total_size )
1045 return RESULT_WRITEFAIL;
1047 for (int i = 0; i < iov->m_Count; ++i) {
1048 MaybeHash (iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1052 *bytes_written = write_size;
1058 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1060 KM_TEST_NULL_L(buf);
1063 if ( bytes_written == 0 )
1064 bytes_written = &tmp_int;
1066 if ( m_Handle == -1L )
1067 return RESULT_STATE;
1069 int write_size = write(m_Handle, buf, buf_len);
1070 MaybeHash (buf, buf_len);
1072 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1073 return RESULT_WRITEFAIL;
1075 *bytes_written = write_size;
1082 //------------------------------------------------------------------------------------------
1087 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1090 ui32_t read_size = 0;
1094 KM_TEST_NULL_STR_L(filename);
1096 Result_t result = File.OpenRead(filename);
1098 if ( KM_SUCCESS(result) )
1100 fsize = File.Size();
1102 if ( fsize > (Kumu::fpos_t)max_size )
1104 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1105 return RESULT_ALLOC;
1110 DefaultLogSink().Error("%s: zero file size\n", filename);
1111 return RESULT_READFAIL;
1114 result = ReadBuf.Capacity((ui32_t)fsize);
1117 if ( KM_SUCCESS(result) )
1118 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1120 if ( KM_SUCCESS(result) )
1121 outString.assign((const char*)ReadBuf.RoData(), read_size);
1129 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1132 ui32_t write_count = 0;
1133 KM_TEST_NULL_STR_L(filename);
1135 Result_t result = File.OpenWrite(filename);
1137 if ( KM_SUCCESS(result) )
1138 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1143 //------------------------------------------------------------------------------------------
1148 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1151 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1152 Result_t result = Buffer.Capacity(file_size);
1154 if ( KM_SUCCESS(result) )
1156 ui32_t read_count = 0;
1159 result = Reader.OpenRead(Filename.c_str());
1161 if ( KM_SUCCESS(result) )
1162 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1164 if ( KM_SUCCESS(result) )
1166 assert(file_size == read_count);
1167 Buffer.Length(read_count);
1168 MemIOReader MemReader(&Buffer);
1169 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1178 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1181 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1183 if ( KM_SUCCESS(result) )
1185 ui32_t write_count = 0;
1187 MemIOWriter MemWriter(&Buffer);
1189 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1191 if ( KM_SUCCESS(result) )
1193 Buffer.Length(MemWriter.Length());
1194 result = Writer.OpenWrite(Filename.c_str());
1197 if ( KM_SUCCESS(result) )
1198 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1204 //------------------------------------------------------------------------------------------
1209 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1211 ui32_t file_size = FileSize(Filename);
1212 Result_t result = Buffer.Capacity(file_size);
1214 if ( KM_SUCCESS(result) )
1216 ui32_t read_count = 0;
1219 result = Reader.OpenRead(Filename.c_str());
1221 if ( KM_SUCCESS(result) )
1222 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1224 if ( KM_SUCCESS(result) )
1226 if ( file_size != read_count)
1227 return RESULT_READFAIL;
1229 Buffer.Length(read_count);
1238 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1240 ui32_t write_count = 0;
1243 Result_t result = Writer.OpenWrite(Filename.c_str());
1245 if ( KM_SUCCESS(result) )
1246 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1248 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1249 return RESULT_WRITEFAIL;
1254 //------------------------------------------------------------------------------------------
1257 Kumu::DirScanner::DirScanner()
1263 Kumu::DirScanner::Open (const char* filename)
1265 KM_TEST_NULL_L (filename);
1267 if (!boost::filesystem::is_directory(filename)) {
1268 return RESULT_NOT_FOUND;
1271 _iterator = boost::filesystem::directory_iterator (filename);
1276 Kumu::DirScanner::GetNext (char* filename)
1278 KM_TEST_NULL_L (filename);
1280 if (_iterator == boost::filesystem::directory_iterator()) {
1281 return RESULT_ENDOFFILE;
1284 #if BOOST_FILESYSTEM_VERSION == 3
1285 std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1287 std::string f = boost::filesystem::path(*_iterator).filename();
1289 strncpy (filename, f.c_str(), MaxFilePath);
1294 //------------------------------------------------------------------------------------------
1297 // Attention Windows users: make sure to use the proper separator character
1298 // with these functions.
1301 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1304 Kumu::CreateDirectoriesInPath(const std::string& Path)
1306 bool abs = PathIsAbsolute(Path);
1307 PathCompList_t PathComps, TmpPathComps;
1309 PathToComponents(Path, PathComps);
1311 while ( ! PathComps.empty() )
1313 TmpPathComps.push_back(PathComps.front());
1314 PathComps.pop_front();
1315 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1317 if ( ! PathIsDirectory(tmp_path) )
1320 if ( _mkdir(tmp_path.c_str()) != 0 )
1322 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1325 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1326 tmp_path.c_str(), strerror(errno));
1327 return RESULT_DIR_CREATE;
1338 Kumu::DeleteFile(const std::string& filename)
1340 if ( _unlink(filename.c_str()) == 0 )
1346 case ENOTDIR: return RESULT_NOTAFILE;
1351 case EPERM: return RESULT_NO_PERM;
1354 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1360 h__DeletePath(const std::string& pathname)
1362 if ( pathname.empty() )
1363 return RESULT_NULL_STR;
1365 Result_t result = RESULT_OK;
1367 if ( ! PathIsDirectory(pathname) )
1369 result = DeleteFile(pathname);
1375 char next_file[Kumu::MaxFilePath];
1376 result = TestDir.Open(pathname.c_str());
1378 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1380 if ( next_file[0] == '.' )
1382 if ( next_file[1] == 0 )
1383 continue; // don't delete 'this'
1385 if ( next_file[1] == '.' && next_file[2] == 0 )
1386 continue; // don't delete 'this' parent
1389 result = h__DeletePath(pathname + std::string("/") + next_file);
1393 if ( _rmdir(pathname.c_str()) != 0 )
1399 result = RESULT_NOTAFILE;
1406 result = RESULT_NO_PERM;
1410 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1411 result = RESULT_FAIL;
1421 Kumu::DeletePath(const std::string& pathname)
1423 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1424 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1425 return h__DeletePath(c_pathname);
1429 //------------------------------------------------------------------------------------------
1434 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1437 ULARGE_INTEGER lTotalNumberOfBytes;
1438 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1440 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1442 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1443 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1446 HRESULT LastError = ::GetLastError();
1448 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1453 if ( statfs(path.c_str(), &s) == 0 )
1455 if ( s.f_blocks < 1 )
1457 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1458 path.c_str(), s.f_blocks);
1462 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1463 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1470 case ENOTDIR: return RESULT_NOTAFILE;
1471 case EACCES: return RESULT_NO_PERM;
1474 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1481 // end KM_fileio.cpp