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 //------------------------------------------------------------------------------------------
682 /** @param filename File name (UTF-8 encoded) */
684 Kumu::FileReader::OpenRead(const char* filename) const
686 KM_TEST_NULL_STR_L(filename);
687 const_cast<FileReader*>(this)->m_Filename = filename;
689 // suppress popup window on error
690 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
692 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename, -1, 0, 0);
693 whar_t* buffer = new wchar_t[wn];
694 if (MultiByteToWideChar (CP_UTF8, 0, filename, -1, buffer, wn)) {
696 return Kumu::RESULT_FAIL;
699 const_cast<FileReader*>(this)->m_Handle = ::CreateFileW(buffer,
700 (GENERIC_READ), // open for reading
701 FILE_SHARE_READ, // share for reading
703 OPEN_EXISTING, // read
704 FILE_ATTRIBUTE_NORMAL, // normal file
705 NULL // no template file
710 ::SetErrorMode(prev);
712 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
713 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
718 Kumu::FileReader::Close() const
720 if ( m_Handle == INVALID_HANDLE_VALUE )
721 return Kumu::RESULT_FILEOPEN;
723 // suppress popup window on error
724 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
725 BOOL result = ::CloseHandle(m_Handle);
726 ::SetErrorMode(prev);
727 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
729 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
734 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
736 if ( m_Handle == INVALID_HANDLE_VALUE )
737 return Kumu::RESULT_STATE;
740 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
741 in.QuadPart = position;
742 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
743 HRESULT LastError = GetLastError();
744 ::SetErrorMode(prev);
746 if ( (LastError != NO_ERROR
747 && (in.LowPart == INVALID_SET_FILE_POINTER
748 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
749 return Kumu::RESULT_READFAIL;
751 return Kumu::RESULT_OK;
756 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
760 if ( m_Handle == INVALID_HANDLE_VALUE )
761 return Kumu::RESULT_FILEOPEN;
764 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
765 in.QuadPart = (__int64)0;
766 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
767 HRESULT LastError = GetLastError();
768 ::SetErrorMode(prev);
770 if ( (LastError != NO_ERROR
771 && (in.LowPart == INVALID_SET_FILE_POINTER
772 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
773 return Kumu::RESULT_READFAIL;
775 *pos = (Kumu::fpos_t)in.QuadPart;
776 return Kumu::RESULT_OK;
781 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
784 Result_t result = Kumu::RESULT_OK;
788 if ( read_count == 0 )
789 read_count = &tmp_int;
793 if ( m_Handle == INVALID_HANDLE_VALUE )
794 return Kumu::RESULT_FILEOPEN;
796 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
797 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
798 result = Kumu::RESULT_READFAIL;
800 ::SetErrorMode(prev);
802 if ( tmp_count == 0 ) /* EOF */
803 result = Kumu::RESULT_ENDOFFILE;
805 if ( KM_SUCCESS(result) )
806 *read_count = tmp_count;
813 //------------------------------------------------------------------------------------------
816 /** @param filename File name (UTF-8 encoded) */
818 Kumu::FileWriter::OpenWrite(const char* filename)
820 KM_TEST_NULL_STR_L(filename);
821 m_Filename = filename;
823 // suppress popup window on error
824 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
826 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename, -1, 0, 0);
827 whar_t* buffer = new wchar_t[wn];
828 if (MultiByteToWideChar (CP_UTF8, 0, filename, -1, buffer, wn)) {
830 return Kumu::RESULT_FAIL;
833 m_Handle = ::CreateFileW(buffer,
834 (GENERIC_WRITE|GENERIC_READ), // open for reading
835 FILE_SHARE_READ, // share for reading
837 CREATE_ALWAYS, // overwrite (beware!)
838 FILE_ATTRIBUTE_NORMAL, // normal file
839 NULL // no template file
844 ::SetErrorMode(prev);
846 if ( m_Handle == INVALID_HANDLE_VALUE )
847 return Kumu::RESULT_FILEOPEN;
849 m_IOVec = new h__iovec;
850 return Kumu::RESULT_OK;
853 /** @param filename File name (UTF-8 encoded) */
855 Kumu::FileWriter::OpenModify(const char* filename)
857 KM_TEST_NULL_STR_L(filename);
858 m_Filename = filename;
860 // suppress popup window on error
861 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
863 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename, -1, 0, 0);
864 whar_t* buffer = new wchar_t[wn];
865 if (MultiByteToWideChar (CP_UTF8, 0, filename, -1, buffer, wn)) {
867 return Kumu::RESULT_FAIL;
870 m_Handle = ::CreateFileW(buffer,
871 (GENERIC_WRITE|GENERIC_READ), // open for reading
872 FILE_SHARE_READ, // share for reading
874 OPEN_ALWAYS, // don't truncate existing
875 FILE_ATTRIBUTE_NORMAL, // normal file
876 NULL // no template file
881 ::SetErrorMode(prev);
883 if ( m_Handle == INVALID_HANDLE_VALUE )
884 return Kumu::RESULT_FILEOPEN;
886 m_IOVec = new h__iovec;
887 return Kumu::RESULT_OK;
892 Kumu::FileWriter::Writev(ui32_t* bytes_written)
894 assert( ! m_IOVec.empty() );
895 register h__iovec* iov = m_IOVec;
898 if ( bytes_written == 0 )
899 bytes_written = &tmp_int;
901 if ( m_Handle == INVALID_HANDLE_VALUE )
902 return Kumu::RESULT_STATE;
905 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
906 Result_t result = Kumu::RESULT_OK;
908 // AFAIK, there is no writev() equivalent in the win32 API
909 for ( register int i = 0; i < iov->m_Count; i++ )
911 ui32_t tmp_count = 0;
912 BOOL wr_result = ::WriteFile(m_Handle,
913 iov->m_iovec[i].iov_base,
914 iov->m_iovec[i].iov_len,
918 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
920 result = Kumu::RESULT_WRITEFAIL;
924 MaybeHash (iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
925 *bytes_written += tmp_count;
928 ::SetErrorMode(prev);
929 iov->m_Count = 0; // error nor not, all is lost
936 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
941 if ( bytes_written == 0 )
942 bytes_written = &tmp_int;
944 if ( m_Handle == INVALID_HANDLE_VALUE )
945 return Kumu::RESULT_STATE;
947 // suppress popup window on error
948 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
949 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
950 ::SetErrorMode(prev);
952 if ( result == 0 || *bytes_written != buf_len )
953 return Kumu::RESULT_WRITEFAIL;
955 MaybeHash (buf, buf_len);
957 return Kumu::RESULT_OK;
961 //------------------------------------------------------------------------------------------
966 Kumu::FileReader::OpenRead(const char* filename) const
968 KM_TEST_NULL_STR_L(filename);
969 const_cast<FileReader*>(this)->m_Filename = filename;
970 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
971 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
976 Kumu::FileReader::Close() const
978 if ( m_Handle == -1L )
979 return RESULT_FILEOPEN;
982 const_cast<FileReader*>(this)->m_Handle = -1L;
988 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
990 if ( m_Handle == -1L )
991 return RESULT_FILEOPEN;
993 if ( lseek(m_Handle, position, whence) == -1L )
994 return RESULT_BADSEEK;
1001 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1003 KM_TEST_NULL_L(pos);
1005 if ( m_Handle == -1L )
1006 return RESULT_FILEOPEN;
1008 Kumu::fpos_t tmp_pos;
1010 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1011 return RESULT_READFAIL;
1019 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1021 KM_TEST_NULL_L(buf);
1022 i32_t tmp_count = 0;
1025 if ( read_count == 0 )
1026 read_count = &tmp_int;
1030 if ( m_Handle == -1L )
1031 return RESULT_FILEOPEN;
1033 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1034 return RESULT_READFAIL;
1036 *read_count = tmp_count;
1037 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1041 //------------------------------------------------------------------------------------------
1046 Kumu::FileWriter::OpenWrite(const char* filename)
1048 KM_TEST_NULL_STR_L(filename);
1049 m_Filename = filename;
1050 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
1052 if ( m_Handle == -1L )
1054 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
1055 return RESULT_FILEOPEN;
1058 m_IOVec = new h__iovec;
1064 Kumu::FileWriter::OpenModify(const char* filename)
1066 KM_TEST_NULL_STR_L(filename);
1067 m_Filename = filename;
1068 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
1070 if ( m_Handle == -1L )
1072 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
1073 return RESULT_FILEOPEN;
1076 m_IOVec = new h__iovec;
1082 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1084 assert( ! m_IOVec.empty() );
1085 register h__iovec* iov = m_IOVec;
1088 if ( bytes_written == 0 )
1089 bytes_written = &tmp_int;
1091 if ( m_Handle == -1L )
1092 return RESULT_STATE;
1095 for ( int i = 0; i < iov->m_Count; i++ )
1096 total_size += iov->m_iovec[i].iov_len;
1098 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1100 if ( write_size == -1L || write_size != total_size )
1101 return RESULT_WRITEFAIL;
1103 for (int i = 0; i < iov->m_Count; ++i) {
1104 MaybeHash (iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1108 *bytes_written = write_size;
1114 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1116 KM_TEST_NULL_L(buf);
1119 if ( bytes_written == 0 )
1120 bytes_written = &tmp_int;
1122 if ( m_Handle == -1L )
1123 return RESULT_STATE;
1125 int write_size = write(m_Handle, buf, buf_len);
1126 MaybeHash (buf, buf_len);
1128 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1129 return RESULT_WRITEFAIL;
1131 *bytes_written = write_size;
1138 //------------------------------------------------------------------------------------------
1143 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1146 ui32_t read_size = 0;
1150 KM_TEST_NULL_STR_L(filename);
1152 Result_t result = File.OpenRead(filename);
1154 if ( KM_SUCCESS(result) )
1156 fsize = File.Size();
1158 if ( fsize > (Kumu::fpos_t)max_size )
1160 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1161 return RESULT_ALLOC;
1166 DefaultLogSink().Error("%s: zero file size\n", filename);
1167 return RESULT_READFAIL;
1170 result = ReadBuf.Capacity((ui32_t)fsize);
1173 if ( KM_SUCCESS(result) )
1174 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1176 if ( KM_SUCCESS(result) )
1177 outString.assign((const char*)ReadBuf.RoData(), read_size);
1185 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1188 ui32_t write_count = 0;
1189 KM_TEST_NULL_STR_L(filename);
1191 Result_t result = File.OpenWrite(filename);
1193 if ( KM_SUCCESS(result) )
1194 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1199 //------------------------------------------------------------------------------------------
1204 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1207 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1208 Result_t result = Buffer.Capacity(file_size);
1210 if ( KM_SUCCESS(result) )
1212 ui32_t read_count = 0;
1215 result = Reader.OpenRead(Filename.c_str());
1217 if ( KM_SUCCESS(result) )
1218 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1220 if ( KM_SUCCESS(result) )
1222 assert(file_size == read_count);
1223 Buffer.Length(read_count);
1224 MemIOReader MemReader(&Buffer);
1225 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1234 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1237 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1239 if ( KM_SUCCESS(result) )
1241 ui32_t write_count = 0;
1243 MemIOWriter MemWriter(&Buffer);
1245 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1247 if ( KM_SUCCESS(result) )
1249 Buffer.Length(MemWriter.Length());
1250 result = Writer.OpenWrite(Filename.c_str());
1253 if ( KM_SUCCESS(result) )
1254 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1260 //------------------------------------------------------------------------------------------
1265 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1267 ui32_t file_size = FileSize(Filename);
1268 Result_t result = Buffer.Capacity(file_size);
1270 if ( KM_SUCCESS(result) )
1272 ui32_t read_count = 0;
1275 result = Reader.OpenRead(Filename.c_str());
1277 if ( KM_SUCCESS(result) )
1278 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1280 if ( KM_SUCCESS(result) )
1282 if ( file_size != read_count)
1283 return RESULT_READFAIL;
1285 Buffer.Length(read_count);
1294 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1296 ui32_t write_count = 0;
1299 Result_t result = Writer.OpenWrite(Filename.c_str());
1301 if ( KM_SUCCESS(result) )
1302 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1304 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1305 return RESULT_WRITEFAIL;
1310 //------------------------------------------------------------------------------------------
1313 Kumu::DirScanner::DirScanner()
1319 Kumu::DirScanner::Open (const char* filename)
1321 KM_TEST_NULL_L (filename);
1323 if (!boost::filesystem::is_directory(filename)) {
1324 return RESULT_NOT_FOUND;
1327 _iterator = boost::filesystem::directory_iterator (filename);
1332 Kumu::DirScanner::GetNext (char* filename)
1334 KM_TEST_NULL_L (filename);
1336 if (_iterator == boost::filesystem::directory_iterator()) {
1337 return RESULT_ENDOFFILE;
1340 #if BOOST_FILESYSTEM_VERSION == 3
1341 std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1343 std::string f = boost::filesystem::path(*_iterator).filename();
1345 strncpy (filename, f.c_str(), MaxFilePath);
1350 //------------------------------------------------------------------------------------------
1353 // Attention Windows users: make sure to use the proper separator character
1354 // with these functions.
1357 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1360 Kumu::CreateDirectoriesInPath(const std::string& Path)
1362 bool abs = PathIsAbsolute(Path);
1363 PathCompList_t PathComps, TmpPathComps;
1365 PathToComponents(Path, PathComps);
1367 while ( ! PathComps.empty() )
1369 TmpPathComps.push_back(PathComps.front());
1370 PathComps.pop_front();
1371 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1373 if ( ! PathIsDirectory(tmp_path) )
1376 if ( _mkdir(tmp_path.c_str()) != 0 )
1378 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1381 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1382 tmp_path.c_str(), strerror(errno));
1383 return RESULT_DIR_CREATE;
1394 Kumu::DeleteFile(const std::string& filename)
1396 if ( _unlink(filename.c_str()) == 0 )
1402 case ENOTDIR: return RESULT_NOTAFILE;
1407 case EPERM: return RESULT_NO_PERM;
1410 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1416 h__DeletePath(const std::string& pathname)
1418 if ( pathname.empty() )
1419 return RESULT_NULL_STR;
1421 Result_t result = RESULT_OK;
1423 if ( ! PathIsDirectory(pathname) )
1425 result = DeleteFile(pathname);
1431 char next_file[Kumu::MaxFilePath];
1432 result = TestDir.Open(pathname.c_str());
1434 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1436 if ( next_file[0] == '.' )
1438 if ( next_file[1] == 0 )
1439 continue; // don't delete 'this'
1441 if ( next_file[1] == '.' && next_file[2] == 0 )
1442 continue; // don't delete 'this' parent
1445 result = h__DeletePath(pathname + std::string("/") + next_file);
1449 if ( _rmdir(pathname.c_str()) != 0 )
1455 result = RESULT_NOTAFILE;
1462 result = RESULT_NO_PERM;
1466 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1467 result = RESULT_FAIL;
1477 Kumu::DeletePath(const std::string& pathname)
1479 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1480 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1481 return h__DeletePath(c_pathname);
1485 //------------------------------------------------------------------------------------------
1490 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1493 ULARGE_INTEGER lTotalNumberOfBytes;
1494 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1496 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1498 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1499 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1502 HRESULT LastError = ::GetLastError();
1504 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1509 if ( statfs(path.c_str(), &s) == 0 )
1511 if ( s.f_blocks < 1 )
1513 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1514 path.c_str(), s.f_blocks);
1518 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1519 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1526 case ENOTDIR: return RESULT_NOTAFILE;
1527 case EACCES: return RESULT_NO_PERM;
1530 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1537 // end KM_fileio.cpp