2 Copyright (c) 2004-2012, 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>
41 #define _getcwd getcwd
42 #define _unlink unlink
49 typedef struct _stati64 fstat_t;
53 // win32 has WriteFileGather() and ReadFileScatter() but they
54 // demand page alignment and page sizing, making them unsuitable
55 // for use with arbitrary buffer sizes.
57 char* iov_base; // stupid iovec uses char*
61 # if defined(__linux__)
62 # include <sys/statfs.h>
64 # include <sys/param.h>
65 # include <sys/mount.h>
70 typedef struct stat fstat_t;
76 do_stat(const char* path, fstat_t* stat_info)
78 KM_TEST_NULL_STR_L(path);
79 KM_TEST_NULL_L(stat_info);
81 Kumu::Result_t result = Kumu::RESULT_OK;
84 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
86 if ( _stati64(path, stat_info) == (__int64)-1 )
87 result = Kumu::RESULT_FILEOPEN;
89 ::SetErrorMode( prev );
91 if ( stat(path, stat_info) == -1L )
92 result = Kumu::RESULT_FILEOPEN;
94 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
95 result = Kumu::RESULT_FILEOPEN;
104 static Kumu::Result_t
105 do_fstat(FileHandle handle, fstat_t* stat_info)
107 KM_TEST_NULL_L(stat_info);
109 Kumu::Result_t result = Kumu::RESULT_OK;
111 if ( fstat(handle, 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;
125 Kumu::PathExists(const std::string& pathname)
127 if ( pathname.empty() )
132 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
140 Kumu::PathIsFile(const std::string& pathname)
142 if ( pathname.empty() )
147 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
149 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
159 Kumu::PathIsDirectory(const std::string& pathname)
161 if ( pathname.empty() )
166 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
168 if ( info.st_mode & S_IFDIR )
177 Kumu::FileSize(const std::string& pathname)
179 if ( pathname.empty() )
184 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
186 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
187 return(info.st_size);
195 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
197 PathCompList_t::const_iterator i;
198 for ( i = in_list.begin(); i != in_list.end(); ++i )
202 if ( ! out_list.empty() )
207 else if ( *i != "." )
209 out_list.push_back(*i);
216 Kumu::PathMakeCanonical(const std::string& Path, char separator)
218 PathCompList_t in_list, out_list;
219 bool is_absolute = PathIsAbsolute(Path, separator);
220 PathToComponents(Path, in_list, separator);
221 make_canonical_list(in_list, out_list);
224 return ComponentsToAbsolutePath(out_list, separator);
226 return ComponentsToPath(out_list, separator);
231 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
233 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
237 Kumu::PathCompList_t&
238 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
242 CList = km_token_split(Path, s);
248 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
253 PathCompList_t::const_iterator ci = CList.begin();
254 std::string out_path = *ci;
256 for ( ci++; ci != CList.end(); ci++ )
257 out_path += separator + *ci;
264 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
266 std::string out_path;
269 out_path = separator;
272 PathCompList_t::const_iterator ci;
274 for ( ci = CList.begin(); ci != CList.end(); ci++ )
275 out_path += separator + *ci;
283 Kumu::PathHasComponents(const std::string& Path, char separator)
285 if ( strchr(Path.c_str(), separator) == 0 )
293 Kumu::PathIsAbsolute(const std::string& Path, char separator)
298 if ( Path[0] == separator)
308 char cwd_buf [MaxFilePath];
309 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
311 DefaultLogSink().Error("Error retrieving current working directory.");
320 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
329 if ( PathIsAbsolute(Path, separator) )
330 return PathMakeCanonical(Path);
332 PathCompList_t in_list, out_list;
333 PathToComponents(PathJoin(PathCwd(), Path), in_list);
334 make_canonical_list(in_list, out_list);
336 return ComponentsToAbsolutePath(out_list);
341 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
343 size_t pos = Path.find(Parent);
345 if ( pos == 0 ) // Parent found at offset 0
346 return Path.substr(Parent.size()+1);
353 Kumu::PathBasename(const std::string& Path, char separator)
355 PathCompList_t CList;
356 PathToComponents(Path, CList, separator);
366 Kumu::PathDirname(const std::string& Path, char separator)
368 PathCompList_t CList;
369 bool is_absolute = PathIsAbsolute(Path, separator);
370 PathToComponents(Path, CList, separator);
373 return is_absolute ? "/" : "";
378 return ComponentsToAbsolutePath(CList, separator);
380 return ComponentsToPath(CList, separator);
385 Kumu::PathGetExtension(const std::string& Path)
387 std::string Basename = PathBasename(Path);
388 const char* p = strrchr(Basename.c_str(), '.');
398 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
400 std::string Basename = PathBasename(Path);
401 const char* p = strrchr(Basename.c_str(), '.');
404 Basename = Basename.substr(0, p - Basename.c_str());
406 if ( Extension.empty() )
409 return Basename + "." + Extension;
414 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
416 return Path1 + separator + Path2;
421 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
423 return Path1 + separator + Path2 + separator + Path3;
428 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
429 const std::string& Path3, const std::string& Path4, char separator)
431 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
435 // returns false if link cannot be read
438 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
440 PathCompList_t in_list, out_list;
441 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
442 PathCompList_t::iterator i;
443 char link_buf[MaxFilePath];
445 for ( i = in_list.begin(); i != in_list.end(); ++i )
447 assert ( *i != ".." && *i != "." );
448 out_list.push_back(*i);
452 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
453 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
455 if ( link_size == -1 )
457 if ( errno == EINVAL )
460 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
464 assert(link_size < MaxFilePath);
465 link_buf[link_size] = 0;
466 std::string tmp_path;
469 if ( PathIsAbsolute(link_buf) )
475 tmp_path = PathJoin(PathDirname(next_link), link_buf);
478 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
482 resolved_path = ComponentsToAbsolutePath(out_list, separator);
487 // TODO: is there a reasonable equivalent to be written for win32?
490 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
492 resolved_path = link_path;
499 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
500 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
502 PathList_t::const_iterator si;
503 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
505 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
507 if ( one_shot && ! FoundPaths.empty() )
516 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
517 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
519 char name_buf[MaxFilePath];
522 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
524 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
526 if ( name_buf[0] == '.' ) continue; // no hidden files
527 std::string tmp_path = SearchDir + separator + name_buf;
529 if ( PathIsDirectory(tmp_path.c_str()) )
530 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
532 else if ( Pattern.Match(name_buf) )
534 FoundPaths.push_back(SearchDir + separator + name_buf);
548 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
550 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
555 regerror(result, &m_regex, buf, 128);
556 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
561 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
562 m_regex = rhs.m_regex;
565 Kumu::PathMatchRegex::~PathMatchRegex() {
570 Kumu::PathMatchRegex::Match(const std::string& s) const {
571 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
577 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
579 std::string regex; // convert glob to regex
581 for ( const char* p = glob.c_str(); *p != 0; p++ )
585 case '.': regex += "\\."; break;
586 case '*': regex += ".*"; break;
587 case '?': regex += ".?"; break;
588 default: regex += *p;
593 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
598 regerror(result, &m_regex, buf, 128);
599 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
604 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
605 m_regex = rhs.m_regex;
608 Kumu::PathMatchGlob::~PathMatchGlob() {
613 Kumu::PathMatchGlob::Match(const std::string& s) const {
614 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
619 //------------------------------------------------------------------------------------------
620 // portable aspects of the file classes
622 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
625 class Kumu::FileWriter::h__iovec
629 struct iovec m_iovec[IOVecMaxEntries];
630 h__iovec() : m_Count(0) {}
637 Kumu::FileReader::Size() const
640 return FileSize(m_Filename.c_str());
644 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
646 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
647 return(info.st_size);
654 // these are declared here instead of in the header file
655 // because we have a mem_ptr that is managing a hidden class
656 Kumu::FileWriter::FileWriter() {}
657 Kumu::FileWriter::~FileWriter() {}
661 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
663 assert( ! m_IOVec.empty() );
664 register h__iovec* iov = m_IOVec;
667 if ( iov->m_Count >= IOVecMaxEntries )
669 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
671 return RESULT_WRITEFAIL;
674 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
675 iov->m_iovec[iov->m_Count].iov_len = buf_len;
683 //------------------------------------------------------------------------------------------
687 Kumu::FileReader::OpenRead(const std::string& filename) const
689 const_cast<FileReader*>(this)->m_Filename = filename;
691 // suppress popup window on error
692 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
694 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename,
695 (GENERIC_READ), // open for reading
696 FILE_SHARE_READ, // share for reading
698 OPEN_EXISTING, // read
699 FILE_ATTRIBUTE_NORMAL, // normal file
700 NULL // no template file
703 ::SetErrorMode(prev);
705 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
706 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
711 Kumu::FileReader::Close() const
713 if ( m_Handle == INVALID_HANDLE_VALUE )
714 return Kumu::RESULT_FILEOPEN;
716 // suppress popup window on error
717 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
718 BOOL result = ::CloseHandle(m_Handle);
719 ::SetErrorMode(prev);
720 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
722 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
727 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
729 if ( m_Handle == INVALID_HANDLE_VALUE )
730 return Kumu::RESULT_STATE;
733 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
734 in.QuadPart = position;
735 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
736 HRESULT LastError = GetLastError();
737 ::SetErrorMode(prev);
739 if ( (LastError != NO_ERROR
740 && (in.LowPart == INVALID_SET_FILE_POINTER
741 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
742 return Kumu::RESULT_READFAIL;
744 return Kumu::RESULT_OK;
749 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
753 if ( m_Handle == INVALID_HANDLE_VALUE )
754 return Kumu::RESULT_FILEOPEN;
757 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
758 in.QuadPart = (__int64)0;
759 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
760 HRESULT LastError = GetLastError();
761 ::SetErrorMode(prev);
763 if ( (LastError != NO_ERROR
764 && (in.LowPart == INVALID_SET_FILE_POINTER
765 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
766 return Kumu::RESULT_READFAIL;
768 *pos = (Kumu::fpos_t)in.QuadPart;
769 return Kumu::RESULT_OK;
774 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
777 Result_t result = Kumu::RESULT_OK;
781 if ( read_count == 0 )
782 read_count = &tmp_int;
786 if ( m_Handle == INVALID_HANDLE_VALUE )
787 return Kumu::RESULT_FILEOPEN;
789 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
790 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
791 result = Kumu::RESULT_READFAIL;
793 ::SetErrorMode(prev);
795 if ( tmp_count == 0 ) /* EOF */
796 result = Kumu::RESULT_ENDOFFILE;
798 if ( KM_SUCCESS(result) )
799 *read_count = tmp_count;
806 //------------------------------------------------------------------------------------------
811 Kumu::FileWriter::OpenWrite(const std::string& filename)
813 m_Filename = filename;
815 // suppress popup window on error
816 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
818 m_Handle = ::CreateFileA(filename.c_str(),
819 (GENERIC_WRITE|GENERIC_READ), // open for reading
820 FILE_SHARE_READ, // share for reading
822 CREATE_ALWAYS, // overwrite (beware!)
823 FILE_ATTRIBUTE_NORMAL, // normal file
824 NULL // no template file
827 ::SetErrorMode(prev);
829 if ( m_Handle == INVALID_HANDLE_VALUE )
830 return Kumu::RESULT_FILEOPEN;
832 m_IOVec = new h__iovec;
833 return Kumu::RESULT_OK;
838 Kumu::FileWriter::Writev(ui32_t* bytes_written)
840 assert( ! m_IOVec.empty() );
841 register h__iovec* iov = m_IOVec;
844 if ( bytes_written == 0 )
845 bytes_written = &tmp_int;
847 if ( m_Handle == INVALID_HANDLE_VALUE )
848 return Kumu::RESULT_STATE;
851 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
852 Result_t result = Kumu::RESULT_OK;
854 // AFAIK, there is no writev() equivalent in the win32 API
855 for ( register int i = 0; i < iov->m_Count; i++ )
857 ui32_t tmp_count = 0;
858 BOOL wr_result = ::WriteFile(m_Handle,
859 iov->m_iovec[i].iov_base,
860 iov->m_iovec[i].iov_len,
864 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
866 result = Kumu::RESULT_WRITEFAIL;
870 *bytes_written += tmp_count;
873 ::SetErrorMode(prev);
874 iov->m_Count = 0; // error nor not, all is lost
881 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
886 if ( bytes_written == 0 )
887 bytes_written = &tmp_int;
889 if ( m_Handle == INVALID_HANDLE_VALUE )
890 return Kumu::RESULT_STATE;
892 // suppress popup window on error
893 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
894 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
895 ::SetErrorMode(prev);
897 if ( result == 0 || *bytes_written != buf_len )
898 return Kumu::RESULT_WRITEFAIL;
900 return Kumu::RESULT_OK;
904 //------------------------------------------------------------------------------------------
909 Kumu::FileReader::OpenRead(const std::string& filename) const
911 const_cast<FileReader*>(this)->m_Filename = filename;
912 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
913 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
918 Kumu::FileReader::Close() const
920 if ( m_Handle == -1L )
921 return RESULT_FILEOPEN;
924 const_cast<FileReader*>(this)->m_Handle = -1L;
930 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
932 if ( m_Handle == -1L )
933 return RESULT_FILEOPEN;
935 if ( lseek(m_Handle, position, whence) == -1L )
936 return RESULT_BADSEEK;
943 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
947 if ( m_Handle == -1L )
948 return RESULT_FILEOPEN;
950 Kumu::fpos_t tmp_pos;
952 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
953 return RESULT_READFAIL;
961 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
967 if ( read_count == 0 )
968 read_count = &tmp_int;
972 if ( m_Handle == -1L )
973 return RESULT_FILEOPEN;
975 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
976 return RESULT_READFAIL;
978 *read_count = tmp_count;
979 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
983 //------------------------------------------------------------------------------------------
988 Kumu::FileWriter::OpenWrite(const std::string& filename)
990 m_Filename = filename;
991 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
993 if ( m_Handle == -1L )
995 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
996 return RESULT_FILEOPEN;
999 m_IOVec = new h__iovec;
1005 Kumu::FileWriter::OpenModify(const std::string& filename)
1007 m_Filename = filename;
1008 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1010 if ( m_Handle == -1L )
1012 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1013 return RESULT_FILEOPEN;
1016 m_IOVec = new h__iovec;
1022 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1024 assert( ! m_IOVec.empty() );
1025 register h__iovec* iov = m_IOVec;
1028 if ( bytes_written == 0 )
1029 bytes_written = &tmp_int;
1031 if ( m_Handle == -1L )
1032 return RESULT_STATE;
1035 for ( int i = 0; i < iov->m_Count; i++ )
1036 total_size += iov->m_iovec[i].iov_len;
1038 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1040 if ( write_size == -1L || write_size != total_size )
1041 return RESULT_WRITEFAIL;
1044 *bytes_written = write_size;
1050 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1052 KM_TEST_NULL_L(buf);
1055 if ( bytes_written == 0 )
1056 bytes_written = &tmp_int;
1058 if ( m_Handle == -1L )
1059 return RESULT_STATE;
1061 int write_size = write(m_Handle, buf, buf_len);
1063 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1064 return RESULT_WRITEFAIL;
1066 *bytes_written = write_size;
1073 //------------------------------------------------------------------------------------------
1078 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1081 ui32_t read_size = 0;
1085 Result_t result = File.OpenRead(filename);
1087 if ( KM_SUCCESS(result) )
1089 fsize = File.Size();
1091 if ( fsize > (Kumu::fpos_t)max_size )
1093 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1094 return RESULT_ALLOC;
1099 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1100 return RESULT_READFAIL;
1103 result = ReadBuf.Capacity((ui32_t)fsize);
1106 if ( KM_SUCCESS(result) )
1107 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1109 if ( KM_SUCCESS(result) )
1110 outString.assign((const char*)ReadBuf.RoData(), read_size);
1118 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1121 ui32_t write_count = 0;
1123 Result_t result = File.OpenWrite(filename);
1125 if ( KM_SUCCESS(result) )
1126 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1131 //------------------------------------------------------------------------------------------
1136 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1139 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1140 Result_t result = Buffer.Capacity(file_size);
1142 if ( KM_SUCCESS(result) )
1144 ui32_t read_count = 0;
1147 result = Reader.OpenRead(Filename);
1149 if ( KM_SUCCESS(result) )
1150 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1152 if ( KM_SUCCESS(result) )
1154 assert(file_size == read_count);
1155 Buffer.Length(read_count);
1156 MemIOReader MemReader(&Buffer);
1157 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1166 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1169 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1171 if ( KM_SUCCESS(result) )
1173 ui32_t write_count = 0;
1175 MemIOWriter MemWriter(&Buffer);
1177 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1179 if ( KM_SUCCESS(result) )
1181 Buffer.Length(MemWriter.Length());
1182 result = Writer.OpenWrite(Filename);
1185 if ( KM_SUCCESS(result) )
1186 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1192 //------------------------------------------------------------------------------------------
1197 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1199 ui32_t file_size = FileSize(Filename);
1200 Result_t result = Buffer.Capacity(file_size);
1202 if ( KM_SUCCESS(result) )
1204 ui32_t read_count = 0;
1207 result = Reader.OpenRead(Filename);
1209 if ( KM_SUCCESS(result) )
1210 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1212 if ( KM_SUCCESS(result) )
1214 if ( file_size != read_count)
1215 return RESULT_READFAIL;
1217 Buffer.Length(read_count);
1226 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1228 ui32_t write_count = 0;
1231 Result_t result = Writer.OpenWrite(Filename);
1233 if ( KM_SUCCESS(result) )
1234 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1236 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1237 return RESULT_WRITEFAIL;
1242 //------------------------------------------------------------------------------------------
1246 // Win32 directory scanner
1251 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1256 Kumu::DirScanner::Open(const std::string& filename)
1258 // we need to append a '*' to read the entire directory
1259 ui32_t fn_len = filename.size();
1260 char* tmp_file = (char*)malloc(fn_len + 8);
1262 if ( tmp_file == 0 )
1263 return RESULT_ALLOC;
1265 strcpy(tmp_file, filename.c_str());
1266 char* p = &tmp_file[fn_len] - 1;
1268 if ( *p != '/' && *p != '\\' )
1278 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1279 Result_t result = RESULT_OK;
1281 if ( m_Handle == -1 )
1282 result = RESULT_NOT_FOUND;
1291 Kumu::DirScanner::Close()
1293 if ( m_Handle == -1 )
1294 return RESULT_FILEOPEN;
1296 if ( _findclose((long)m_Handle) == -1 )
1304 // This sets filename param to the same per-instance buffer every time, so
1305 // the value will change on the next call
1307 Kumu::DirScanner::GetNext(char* filename)
1309 KM_TEST_NULL_L(filename);
1311 if ( m_Handle == -1 )
1312 return RESULT_FILEOPEN;
1314 if ( m_FileInfo.name[0] == '\0' )
1315 return RESULT_ENDOFFILE;
1317 strncpy(filename, m_FileInfo.name, MaxFilePath);
1318 Result_t result = RESULT_OK;
1320 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1322 m_FileInfo.name[0] = '\0';
1324 if ( errno != ENOENT )
1325 result = RESULT_FAIL;
1334 // POSIX directory scanner
1337 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1341 Kumu::DirScanner::Open(const std::string& dirname)
1343 Result_t result = RESULT_OK;
1345 if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1351 result = RESULT_NOTAFILE;
1353 result = RESULT_NO_PERM;
1356 result = RESULT_PARAM;
1359 result = RESULT_STATE;
1361 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1362 result = RESULT_FAIL;
1372 Kumu::DirScanner::Close()
1374 if ( m_Handle == NULL )
1375 return RESULT_FILEOPEN;
1377 if ( closedir(m_Handle) == -1 ) {
1382 return RESULT_STATE;
1384 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1396 Kumu::DirScanner::GetNext(char* filename)
1398 KM_TEST_NULL_L(filename);
1400 if ( m_Handle == NULL )
1401 return RESULT_FILEOPEN;
1403 struct dirent* entry;
1407 if ( ( entry = readdir(m_Handle)) == NULL )
1408 return RESULT_ENDOFFILE;
1413 strncpy(filename, entry->d_name, MaxFilePath);
1419 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1423 Kumu::DirScannerEx::Open(const std::string& dirname)
1425 Result_t result = RESULT_OK;
1427 if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1433 result = RESULT_NOTAFILE;
1435 result = RESULT_NO_PERM;
1438 result = RESULT_PARAM;
1441 result = RESULT_STATE;
1443 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1444 result = RESULT_FAIL;
1448 if ( KM_SUCCESS(result) )
1449 m_Dirname = dirname;
1451 KM_RESULT_STATE_TEST_IMPLICIT();
1457 Kumu::DirScannerEx::Close()
1459 if ( m_Handle == NULL )
1460 return RESULT_FILEOPEN;
1462 if ( closedir(m_Handle) == -1 )
1468 KM_RESULT_STATE_HERE();
1469 return RESULT_STATE;
1472 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1483 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1485 if ( m_Handle == 0 )
1486 return RESULT_FILEOPEN;
1488 struct dirent* entry;
1492 if ( ( entry = readdir(m_Handle) ) == 0 )
1493 return RESULT_ENDOFFILE;
1498 next_item_name.assign(entry->d_name, strlen(entry->d_name));
1500 switch ( entry->d_type )
1503 next_item_type = DET_DIR;
1507 next_item_type = DET_FILE;
1511 next_item_type = DET_LINK;
1515 next_item_type = DET_DEV;
1525 //------------------------------------------------------------------------------------------
1528 // Attention Windows users: make sure to use the proper separator character
1529 // with these functions.
1532 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1535 Kumu::CreateDirectoriesInPath(const std::string& Path)
1537 bool abs = PathIsAbsolute(Path);
1538 PathCompList_t PathComps, TmpPathComps;
1540 PathToComponents(Path, PathComps);
1542 while ( ! PathComps.empty() )
1544 TmpPathComps.push_back(PathComps.front());
1545 PathComps.pop_front();
1546 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1548 if ( ! PathIsDirectory(tmp_path) )
1551 if ( _mkdir(tmp_path.c_str()) != 0 )
1553 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1556 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1557 tmp_path.c_str(), strerror(errno));
1558 return RESULT_DIR_CREATE;
1569 Kumu::DeleteFile(const std::string& filename)
1571 if ( _unlink(filename.c_str()) == 0 )
1577 case ENOTDIR: return RESULT_NOTAFILE;
1582 case EPERM: return RESULT_NO_PERM;
1585 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1591 h__DeletePath(const std::string& pathname)
1593 if ( pathname.empty() )
1594 return RESULT_NULL_STR;
1596 Result_t result = RESULT_OK;
1598 if ( ! PathIsDirectory(pathname) )
1600 result = DeleteFile(pathname);
1606 char next_file[Kumu::MaxFilePath];
1607 result = TestDir.Open(pathname.c_str());
1609 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1611 if ( next_file[0] == '.' )
1613 if ( next_file[1] == 0 )
1614 continue; // don't delete 'this'
1616 if ( next_file[1] == '.' && next_file[2] == 0 )
1617 continue; // don't delete 'this' parent
1620 result = h__DeletePath(pathname + std::string("/") + next_file);
1624 if ( _rmdir(pathname.c_str()) != 0 )
1630 result = RESULT_NOTAFILE;
1637 result = RESULT_NO_PERM;
1641 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1642 result = RESULT_FAIL;
1652 Kumu::DeletePath(const std::string& pathname)
1654 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1655 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1656 return h__DeletePath(c_pathname);
1660 //------------------------------------------------------------------------------------------
1665 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1668 ULARGE_INTEGER lTotalNumberOfBytes;
1669 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1671 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1673 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1674 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1677 HRESULT LastError = ::GetLastError();
1679 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1684 if ( statfs(path.c_str(), &s) == 0 )
1686 if ( s.f_blocks < 1 )
1688 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1689 path.c_str(), s.f_blocks);
1693 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1694 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1701 case ENOTDIR: return RESULT_NOTAFILE;
1702 case EACCES: return RESULT_NO_PERM;
1705 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1712 // end KM_fileio.cpp