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 wchar_t buffer[1024];
693 if (MultiByteToWideChar (CP_UTF8, MB_PRECOMPOSED, filename, -1, buffer, 1024)) {
694 return Kumu::RESULT_FAIL;
697 const_cast<FileReader*>(this)->m_Handle = ::CreateFileW(buffer,
698 (GENERIC_READ), // open for reading
699 FILE_SHARE_READ, // share for reading
701 OPEN_EXISTING, // read
702 FILE_ATTRIBUTE_NORMAL, // normal file
703 NULL // no template file
706 ::SetErrorMode(prev);
708 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
709 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
714 Kumu::FileReader::Close() const
716 if ( m_Handle == INVALID_HANDLE_VALUE )
717 return Kumu::RESULT_FILEOPEN;
719 // suppress popup window on error
720 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
721 BOOL result = ::CloseHandle(m_Handle);
722 ::SetErrorMode(prev);
723 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
725 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
730 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
732 if ( m_Handle == INVALID_HANDLE_VALUE )
733 return Kumu::RESULT_STATE;
736 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
737 in.QuadPart = position;
738 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
739 HRESULT LastError = GetLastError();
740 ::SetErrorMode(prev);
742 if ( (LastError != NO_ERROR
743 && (in.LowPart == INVALID_SET_FILE_POINTER
744 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
745 return Kumu::RESULT_READFAIL;
747 return Kumu::RESULT_OK;
752 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
756 if ( m_Handle == INVALID_HANDLE_VALUE )
757 return Kumu::RESULT_FILEOPEN;
760 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
761 in.QuadPart = (__int64)0;
762 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
763 HRESULT LastError = GetLastError();
764 ::SetErrorMode(prev);
766 if ( (LastError != NO_ERROR
767 && (in.LowPart == INVALID_SET_FILE_POINTER
768 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
769 return Kumu::RESULT_READFAIL;
771 *pos = (Kumu::fpos_t)in.QuadPart;
772 return Kumu::RESULT_OK;
777 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
780 Result_t result = Kumu::RESULT_OK;
784 if ( read_count == 0 )
785 read_count = &tmp_int;
789 if ( m_Handle == INVALID_HANDLE_VALUE )
790 return Kumu::RESULT_FILEOPEN;
792 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
793 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
794 result = Kumu::RESULT_READFAIL;
796 ::SetErrorMode(prev);
798 if ( tmp_count == 0 ) /* EOF */
799 result = Kumu::RESULT_ENDOFFILE;
801 if ( KM_SUCCESS(result) )
802 *read_count = tmp_count;
809 //------------------------------------------------------------------------------------------
812 /** @param filename File name (UTF-8 encoded) */
814 Kumu::FileWriter::OpenWrite(const char* filename)
816 KM_TEST_NULL_STR_L(filename);
817 m_Filename = filename;
819 // suppress popup window on error
820 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
822 wchar_t buffer[1024];
823 if (MultiByteToWideChar (CP_UTF8, MB_PRECOMPOSED, filename, -1, buffer, 1024)) {
824 return Kumu::RESULT_FAIL;
827 m_Handle = ::CreateFileW(buffer,
828 (GENERIC_WRITE|GENERIC_READ), // open for reading
829 FILE_SHARE_READ, // share for reading
831 CREATE_ALWAYS, // overwrite (beware!)
832 FILE_ATTRIBUTE_NORMAL, // normal file
833 NULL // no template file
836 ::SetErrorMode(prev);
838 if ( m_Handle == INVALID_HANDLE_VALUE )
839 return Kumu::RESULT_FILEOPEN;
841 m_IOVec = new h__iovec;
842 return Kumu::RESULT_OK;
845 /** @param filename File name (UTF-8 encoded) */
847 Kumu::FileWriter::OpenModify(const char* filename)
849 KM_TEST_NULL_STR_L(filename);
850 m_Filename = filename;
852 // suppress popup window on error
853 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
855 wchar_t buffer[1024];
856 if (MultiByteToWideChar (CP_UTF8, MB_PRECOMPOSED, filename, -1, buffer, 1024)) {
857 return Kumu::RESULT_FAIL;
860 m_Handle = ::CreateFileW(buffer,
861 (GENERIC_WRITE|GENERIC_READ), // open for reading
862 FILE_SHARE_READ, // share for reading
864 OPEN_ALWAYS, // don't truncate existing
865 FILE_ATTRIBUTE_NORMAL, // normal file
866 NULL // no template file
869 ::SetErrorMode(prev);
871 if ( m_Handle == INVALID_HANDLE_VALUE )
872 return Kumu::RESULT_FILEOPEN;
874 m_IOVec = new h__iovec;
875 return Kumu::RESULT_OK;
880 Kumu::FileWriter::Writev(ui32_t* bytes_written)
882 assert( ! m_IOVec.empty() );
883 register h__iovec* iov = m_IOVec;
886 if ( bytes_written == 0 )
887 bytes_written = &tmp_int;
889 if ( m_Handle == INVALID_HANDLE_VALUE )
890 return Kumu::RESULT_STATE;
893 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
894 Result_t result = Kumu::RESULT_OK;
896 // AFAIK, there is no writev() equivalent in the win32 API
897 for ( register int i = 0; i < iov->m_Count; i++ )
899 ui32_t tmp_count = 0;
900 BOOL wr_result = ::WriteFile(m_Handle,
901 iov->m_iovec[i].iov_base,
902 iov->m_iovec[i].iov_len,
906 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
908 result = Kumu::RESULT_WRITEFAIL;
912 MaybeHash (iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
913 *bytes_written += tmp_count;
916 ::SetErrorMode(prev);
917 iov->m_Count = 0; // error nor not, all is lost
924 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
929 if ( bytes_written == 0 )
930 bytes_written = &tmp_int;
932 if ( m_Handle == INVALID_HANDLE_VALUE )
933 return Kumu::RESULT_STATE;
935 // suppress popup window on error
936 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
937 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
938 ::SetErrorMode(prev);
940 if ( result == 0 || *bytes_written != buf_len )
941 return Kumu::RESULT_WRITEFAIL;
943 MaybeHash (buf, buf_len);
945 return Kumu::RESULT_OK;
949 //------------------------------------------------------------------------------------------
954 Kumu::FileReader::OpenRead(const char* filename) const
956 KM_TEST_NULL_STR_L(filename);
957 const_cast<FileReader*>(this)->m_Filename = filename;
958 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
959 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
964 Kumu::FileReader::Close() const
966 if ( m_Handle == -1L )
967 return RESULT_FILEOPEN;
970 const_cast<FileReader*>(this)->m_Handle = -1L;
976 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
978 if ( m_Handle == -1L )
979 return RESULT_FILEOPEN;
981 if ( lseek(m_Handle, position, whence) == -1L )
982 return RESULT_BADSEEK;
989 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
993 if ( m_Handle == -1L )
994 return RESULT_FILEOPEN;
996 Kumu::fpos_t tmp_pos;
998 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
999 return RESULT_READFAIL;
1007 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1009 KM_TEST_NULL_L(buf);
1010 i32_t tmp_count = 0;
1013 if ( read_count == 0 )
1014 read_count = &tmp_int;
1018 if ( m_Handle == -1L )
1019 return RESULT_FILEOPEN;
1021 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1022 return RESULT_READFAIL;
1024 *read_count = tmp_count;
1025 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1029 //------------------------------------------------------------------------------------------
1034 Kumu::FileWriter::OpenWrite(const char* filename)
1036 KM_TEST_NULL_STR_L(filename);
1037 m_Filename = filename;
1038 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
1040 if ( m_Handle == -1L )
1042 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
1043 return RESULT_FILEOPEN;
1046 m_IOVec = new h__iovec;
1052 Kumu::FileWriter::OpenModify(const char* filename)
1054 KM_TEST_NULL_STR_L(filename);
1055 m_Filename = filename;
1056 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
1058 if ( m_Handle == -1L )
1060 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
1061 return RESULT_FILEOPEN;
1064 m_IOVec = new h__iovec;
1070 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1072 assert( ! m_IOVec.empty() );
1073 register h__iovec* iov = m_IOVec;
1076 if ( bytes_written == 0 )
1077 bytes_written = &tmp_int;
1079 if ( m_Handle == -1L )
1080 return RESULT_STATE;
1083 for ( int i = 0; i < iov->m_Count; i++ )
1084 total_size += iov->m_iovec[i].iov_len;
1086 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1088 if ( write_size == -1L || write_size != total_size )
1089 return RESULT_WRITEFAIL;
1091 for (int i = 0; i < iov->m_Count; ++i) {
1092 MaybeHash (iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1096 *bytes_written = write_size;
1102 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1104 KM_TEST_NULL_L(buf);
1107 if ( bytes_written == 0 )
1108 bytes_written = &tmp_int;
1110 if ( m_Handle == -1L )
1111 return RESULT_STATE;
1113 int write_size = write(m_Handle, buf, buf_len);
1114 MaybeHash (buf, buf_len);
1116 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1117 return RESULT_WRITEFAIL;
1119 *bytes_written = write_size;
1126 //------------------------------------------------------------------------------------------
1131 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1134 ui32_t read_size = 0;
1138 KM_TEST_NULL_STR_L(filename);
1140 Result_t result = File.OpenRead(filename);
1142 if ( KM_SUCCESS(result) )
1144 fsize = File.Size();
1146 if ( fsize > (Kumu::fpos_t)max_size )
1148 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1149 return RESULT_ALLOC;
1154 DefaultLogSink().Error("%s: zero file size\n", filename);
1155 return RESULT_READFAIL;
1158 result = ReadBuf.Capacity((ui32_t)fsize);
1161 if ( KM_SUCCESS(result) )
1162 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1164 if ( KM_SUCCESS(result) )
1165 outString.assign((const char*)ReadBuf.RoData(), read_size);
1173 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1176 ui32_t write_count = 0;
1177 KM_TEST_NULL_STR_L(filename);
1179 Result_t result = File.OpenWrite(filename);
1181 if ( KM_SUCCESS(result) )
1182 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1187 //------------------------------------------------------------------------------------------
1192 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1195 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1196 Result_t result = Buffer.Capacity(file_size);
1198 if ( KM_SUCCESS(result) )
1200 ui32_t read_count = 0;
1203 result = Reader.OpenRead(Filename.c_str());
1205 if ( KM_SUCCESS(result) )
1206 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1208 if ( KM_SUCCESS(result) )
1210 assert(file_size == read_count);
1211 Buffer.Length(read_count);
1212 MemIOReader MemReader(&Buffer);
1213 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1222 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1225 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1227 if ( KM_SUCCESS(result) )
1229 ui32_t write_count = 0;
1231 MemIOWriter MemWriter(&Buffer);
1233 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1235 if ( KM_SUCCESS(result) )
1237 Buffer.Length(MemWriter.Length());
1238 result = Writer.OpenWrite(Filename.c_str());
1241 if ( KM_SUCCESS(result) )
1242 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1248 //------------------------------------------------------------------------------------------
1253 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1255 ui32_t file_size = FileSize(Filename);
1256 Result_t result = Buffer.Capacity(file_size);
1258 if ( KM_SUCCESS(result) )
1260 ui32_t read_count = 0;
1263 result = Reader.OpenRead(Filename.c_str());
1265 if ( KM_SUCCESS(result) )
1266 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1268 if ( KM_SUCCESS(result) )
1270 if ( file_size != read_count)
1271 return RESULT_READFAIL;
1273 Buffer.Length(read_count);
1282 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1284 ui32_t write_count = 0;
1287 Result_t result = Writer.OpenWrite(Filename.c_str());
1289 if ( KM_SUCCESS(result) )
1290 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1292 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1293 return RESULT_WRITEFAIL;
1298 //------------------------------------------------------------------------------------------
1301 Kumu::DirScanner::DirScanner()
1307 Kumu::DirScanner::Open (const char* filename)
1309 KM_TEST_NULL_L (filename);
1311 if (!boost::filesystem::is_directory(filename)) {
1312 return RESULT_NOT_FOUND;
1315 _iterator = boost::filesystem::directory_iterator (filename);
1320 Kumu::DirScanner::GetNext (char* filename)
1322 KM_TEST_NULL_L (filename);
1324 if (_iterator == boost::filesystem::directory_iterator()) {
1325 return RESULT_ENDOFFILE;
1328 #if BOOST_FILESYSTEM_VERSION == 3
1329 std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1331 std::string f = boost::filesystem::path(*_iterator).filename();
1333 strncpy (filename, f.c_str(), MaxFilePath);
1338 //------------------------------------------------------------------------------------------
1341 // Attention Windows users: make sure to use the proper separator character
1342 // with these functions.
1345 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1348 Kumu::CreateDirectoriesInPath(const std::string& Path)
1350 bool abs = PathIsAbsolute(Path);
1351 PathCompList_t PathComps, TmpPathComps;
1353 PathToComponents(Path, PathComps);
1355 while ( ! PathComps.empty() )
1357 TmpPathComps.push_back(PathComps.front());
1358 PathComps.pop_front();
1359 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1361 if ( ! PathIsDirectory(tmp_path) )
1364 if ( _mkdir(tmp_path.c_str()) != 0 )
1366 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;
1382 Kumu::DeleteFile(const std::string& filename)
1384 if ( _unlink(filename.c_str()) == 0 )
1390 case ENOTDIR: return RESULT_NOTAFILE;
1395 case EPERM: return RESULT_NO_PERM;
1398 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1404 h__DeletePath(const std::string& pathname)
1406 if ( pathname.empty() )
1407 return RESULT_NULL_STR;
1409 Result_t result = RESULT_OK;
1411 if ( ! PathIsDirectory(pathname) )
1413 result = DeleteFile(pathname);
1419 char next_file[Kumu::MaxFilePath];
1420 result = TestDir.Open(pathname.c_str());
1422 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1424 if ( next_file[0] == '.' )
1426 if ( next_file[1] == 0 )
1427 continue; // don't delete 'this'
1429 if ( next_file[1] == '.' && next_file[2] == 0 )
1430 continue; // don't delete 'this' parent
1433 result = h__DeletePath(pathname + std::string("/") + next_file);
1437 if ( _rmdir(pathname.c_str()) != 0 )
1443 result = RESULT_NOTAFILE;
1450 result = RESULT_NO_PERM;
1454 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1455 result = RESULT_FAIL;
1465 Kumu::DeletePath(const std::string& pathname)
1467 std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1468 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1469 return h__DeletePath(c_pathname);
1473 //------------------------------------------------------------------------------------------
1478 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1481 ULARGE_INTEGER lTotalNumberOfBytes;
1482 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1484 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1486 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1487 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1490 HRESULT LastError = ::GetLastError();
1492 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1497 if ( statfs(path.c_str(), &s) == 0 )
1499 if ( s.f_blocks < 1 )
1501 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1502 path.c_str(), s.f_blocks);
1506 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1507 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1514 case ENOTDIR: return RESULT_NOTAFILE;
1515 case EACCES: return RESULT_NO_PERM;
1518 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1525 // end KM_fileio.cpp