2 Copyright (c) 2004-2014, 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& component_list, char separator)
242 PathCompList_t tmp_list = km_token_split(path, std::string(s));
243 PathCompList_t::const_iterator i;
245 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
249 component_list.push_back(*i);
253 return component_list;
258 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
263 PathCompList_t::const_iterator ci = CList.begin();
264 std::string out_path = *ci;
266 for ( ci++; ci != CList.end(); ci++ )
267 out_path += separator + *ci;
274 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
276 std::string out_path;
279 out_path = separator;
282 PathCompList_t::const_iterator ci;
284 for ( ci = CList.begin(); ci != CList.end(); ci++ )
285 out_path += separator + *ci;
293 Kumu::PathHasComponents(const std::string& Path, char separator)
295 if ( strchr(Path.c_str(), separator) == 0 )
303 Kumu::PathIsAbsolute(const std::string& Path, char separator)
308 if ( Path[0] == separator)
318 char cwd_buf [MaxFilePath];
319 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
321 DefaultLogSink().Error("Error retrieving current working directory.");
330 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
339 if ( PathIsAbsolute(Path, separator) )
340 return PathMakeCanonical(Path);
342 PathCompList_t in_list, out_list;
343 PathToComponents(PathJoin(PathCwd(), Path), in_list);
344 make_canonical_list(in_list, out_list);
346 return ComponentsToAbsolutePath(out_list);
351 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
353 size_t pos = Path.find(Parent);
355 if ( pos == 0 ) // Parent found at offset 0
356 return Path.substr(Parent.size()+1);
363 Kumu::PathBasename(const std::string& Path, char separator)
365 PathCompList_t CList;
366 PathToComponents(Path, CList, separator);
376 Kumu::PathDirname(const std::string& Path, char separator)
378 PathCompList_t CList;
379 bool is_absolute = PathIsAbsolute(Path, separator);
380 PathToComponents(Path, CList, separator);
383 return is_absolute ? "/" : "";
388 return ComponentsToAbsolutePath(CList, separator);
390 return ComponentsToPath(CList, separator);
395 Kumu::PathGetExtension(const std::string& Path)
397 std::string Basename = PathBasename(Path);
398 const char* p = strrchr(Basename.c_str(), '.');
408 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
410 std::string Basename = PathBasename(Path);
411 const char* p = strrchr(Basename.c_str(), '.');
414 Basename = Basename.substr(0, p - Basename.c_str());
416 if ( Extension.empty() )
419 return Basename + "." + Extension;
424 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
426 return Path1 + separator + Path2;
431 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
433 return Path1 + separator + Path2 + separator + Path3;
438 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
439 const std::string& Path3, const std::string& Path4, char separator)
441 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
445 // returns false if link cannot be read
448 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
450 PathCompList_t in_list, out_list;
451 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
452 PathCompList_t::iterator i;
453 char link_buf[MaxFilePath];
455 for ( i = in_list.begin(); i != in_list.end(); ++i )
457 assert ( *i != ".." && *i != "." );
458 out_list.push_back(*i);
462 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
463 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
465 if ( link_size == -1 )
467 if ( errno == EINVAL )
470 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
474 assert(link_size < MaxFilePath);
475 link_buf[link_size] = 0;
476 std::string tmp_path;
479 if ( PathIsAbsolute(link_buf) )
485 tmp_path = PathJoin(PathDirname(next_link), link_buf);
488 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
492 resolved_path = ComponentsToAbsolutePath(out_list, separator);
497 // TODO: is there a reasonable equivalent to be written for win32?
500 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
502 resolved_path = link_path;
509 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
510 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
512 PathList_t::const_iterator si;
513 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
515 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
517 if ( one_shot && ! FoundPaths.empty() )
526 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
527 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
529 char name_buf[MaxFilePath];
532 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
534 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
536 if ( name_buf[0] == '.' ) continue; // no hidden files
537 std::string tmp_path = SearchDir + separator + name_buf;
539 if ( PathIsDirectory(tmp_path.c_str()) )
540 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
542 else if ( Pattern.Match(name_buf) )
544 FoundPaths.push_back(SearchDir + separator + name_buf);
558 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
560 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
565 regerror(result, &m_regex, buf, 128);
566 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
571 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
572 m_regex = rhs.m_regex;
575 Kumu::PathMatchRegex::~PathMatchRegex() {
580 Kumu::PathMatchRegex::Match(const std::string& s) const {
581 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
587 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
589 std::string regex; // convert glob to regex
591 for ( const char* p = glob.c_str(); *p != 0; p++ )
595 case '.': regex += "\\."; break;
596 case '*': regex += ".*"; break;
597 case '?': regex += ".?"; break;
598 default: regex += *p;
603 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
608 regerror(result, &m_regex, buf, 128);
609 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
614 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
615 m_regex = rhs.m_regex;
618 Kumu::PathMatchGlob::~PathMatchGlob() {
623 Kumu::PathMatchGlob::Match(const std::string& s) const {
624 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
629 //------------------------------------------------------------------------------------------
630 // portable aspects of the file classes
632 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
635 class Kumu::FileWriter::h__iovec
639 struct iovec m_iovec[IOVecMaxEntries];
640 h__iovec() : m_Count(0) {}
647 Kumu::FileReader::Size() const
650 return FileSize(m_Filename.c_str());
654 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
656 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
657 return(info.st_size);
664 // these are declared here instead of in the header file
665 // because we have a mem_ptr that is managing a hidden class
666 Kumu::FileWriter::FileWriter() {}
667 Kumu::FileWriter::~FileWriter() {}
671 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
673 assert( ! m_IOVec.empty() );
674 register h__iovec* iov = m_IOVec;
677 if ( iov->m_Count >= IOVecMaxEntries )
679 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
681 return RESULT_WRITEFAIL;
684 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
685 iov->m_iovec[iov->m_Count].iov_len = buf_len;
693 //------------------------------------------------------------------------------------------
697 Kumu::FileReader::OpenRead(const std::string& filename) const
699 const_cast<FileReader*>(this)->m_Filename = filename;
701 // suppress popup window on error
702 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
704 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename.c_str(),
705 (GENERIC_READ), // open for reading
706 FILE_SHARE_READ, // share for reading
708 OPEN_EXISTING, // read
709 FILE_ATTRIBUTE_NORMAL, // normal file
710 NULL // no template file
713 ::SetErrorMode(prev);
715 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
716 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
721 Kumu::FileReader::Close() const
723 if ( m_Handle == INVALID_HANDLE_VALUE )
724 return Kumu::RESULT_FILEOPEN;
726 // suppress popup window on error
727 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
728 BOOL result = ::CloseHandle(m_Handle);
729 ::SetErrorMode(prev);
730 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
732 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
737 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
739 if ( m_Handle == INVALID_HANDLE_VALUE )
740 return Kumu::RESULT_STATE;
743 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
744 in.QuadPart = position;
745 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
746 HRESULT LastError = GetLastError();
747 ::SetErrorMode(prev);
749 if ( (LastError != NO_ERROR
750 && (in.LowPart == INVALID_SET_FILE_POINTER
751 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
752 return Kumu::RESULT_READFAIL;
754 return Kumu::RESULT_OK;
759 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
763 if ( m_Handle == INVALID_HANDLE_VALUE )
764 return Kumu::RESULT_FILEOPEN;
767 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
768 in.QuadPart = (__int64)0;
769 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
770 HRESULT LastError = GetLastError();
771 ::SetErrorMode(prev);
773 if ( (LastError != NO_ERROR
774 && (in.LowPart == INVALID_SET_FILE_POINTER
775 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
776 return Kumu::RESULT_READFAIL;
778 *pos = (Kumu::fpos_t)in.QuadPart;
779 return Kumu::RESULT_OK;
784 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
787 Result_t result = Kumu::RESULT_OK;
791 if ( read_count == 0 )
792 read_count = &tmp_int;
796 if ( m_Handle == INVALID_HANDLE_VALUE )
797 return Kumu::RESULT_FILEOPEN;
799 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
800 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
801 result = Kumu::RESULT_READFAIL;
803 ::SetErrorMode(prev);
805 if ( tmp_count == 0 ) /* EOF */
806 result = Kumu::RESULT_ENDOFFILE;
808 if ( KM_SUCCESS(result) )
809 *read_count = tmp_count;
816 //------------------------------------------------------------------------------------------
821 Kumu::FileWriter::OpenWrite(const std::string& filename)
823 m_Filename = filename;
825 // suppress popup window on error
826 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
828 m_Handle = ::CreateFileA(filename.c_str(),
829 (GENERIC_WRITE|GENERIC_READ), // open for reading
830 FILE_SHARE_READ, // share for reading
832 CREATE_ALWAYS, // overwrite (beware!)
833 FILE_ATTRIBUTE_NORMAL, // normal file
834 NULL // no template file
837 ::SetErrorMode(prev);
839 if ( m_Handle == INVALID_HANDLE_VALUE )
840 return Kumu::RESULT_FILEOPEN;
842 m_IOVec = new h__iovec;
843 return Kumu::RESULT_OK;
848 Kumu::FileWriter::Writev(ui32_t* bytes_written)
850 assert( ! m_IOVec.empty() );
851 register h__iovec* iov = m_IOVec;
854 if ( bytes_written == 0 )
855 bytes_written = &tmp_int;
857 if ( m_Handle == INVALID_HANDLE_VALUE )
858 return Kumu::RESULT_STATE;
861 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
862 Result_t result = Kumu::RESULT_OK;
864 // AFAIK, there is no writev() equivalent in the win32 API
865 for ( register int i = 0; i < iov->m_Count; i++ )
867 ui32_t tmp_count = 0;
868 BOOL wr_result = ::WriteFile(m_Handle,
869 iov->m_iovec[i].iov_base,
870 iov->m_iovec[i].iov_len,
874 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
876 result = Kumu::RESULT_WRITEFAIL;
880 *bytes_written += tmp_count;
883 ::SetErrorMode(prev);
884 iov->m_Count = 0; // error nor not, all is lost
891 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
896 if ( bytes_written == 0 )
897 bytes_written = &tmp_int;
899 if ( m_Handle == INVALID_HANDLE_VALUE )
900 return Kumu::RESULT_STATE;
902 // suppress popup window on error
903 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
904 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
905 ::SetErrorMode(prev);
907 if ( result == 0 || *bytes_written != buf_len )
908 return Kumu::RESULT_WRITEFAIL;
910 return Kumu::RESULT_OK;
914 //------------------------------------------------------------------------------------------
919 Kumu::FileReader::OpenRead(const std::string& filename) const
921 const_cast<FileReader*>(this)->m_Filename = filename;
922 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
923 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
928 Kumu::FileReader::Close() const
930 if ( m_Handle == -1L )
931 return RESULT_FILEOPEN;
934 const_cast<FileReader*>(this)->m_Handle = -1L;
940 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
942 if ( m_Handle == -1L )
943 return RESULT_FILEOPEN;
945 if ( lseek(m_Handle, position, whence) == -1L )
946 return RESULT_BADSEEK;
953 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
957 if ( m_Handle == -1L )
958 return RESULT_FILEOPEN;
960 Kumu::fpos_t tmp_pos;
962 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
963 return RESULT_READFAIL;
971 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
977 if ( read_count == 0 )
978 read_count = &tmp_int;
982 if ( m_Handle == -1L )
983 return RESULT_FILEOPEN;
985 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
986 return RESULT_READFAIL;
988 *read_count = tmp_count;
989 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
993 //------------------------------------------------------------------------------------------
998 Kumu::FileWriter::OpenWrite(const std::string& filename)
1000 m_Filename = filename;
1001 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
1003 if ( m_Handle == -1L )
1005 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1006 return RESULT_FILEOPEN;
1009 m_IOVec = new h__iovec;
1015 Kumu::FileWriter::OpenModify(const std::string& filename)
1017 m_Filename = filename;
1018 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1020 if ( m_Handle == -1L )
1022 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1023 return RESULT_FILEOPEN;
1026 m_IOVec = new h__iovec;
1032 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1034 assert( ! m_IOVec.empty() );
1035 register h__iovec* iov = m_IOVec;
1038 if ( bytes_written == 0 )
1039 bytes_written = &tmp_int;
1041 if ( m_Handle == -1L )
1042 return RESULT_STATE;
1045 for ( int i = 0; i < iov->m_Count; i++ )
1046 total_size += iov->m_iovec[i].iov_len;
1048 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1050 if ( write_size == -1L || write_size != total_size )
1051 return RESULT_WRITEFAIL;
1054 *bytes_written = write_size;
1060 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1062 KM_TEST_NULL_L(buf);
1065 if ( bytes_written == 0 )
1066 bytes_written = &tmp_int;
1068 if ( m_Handle == -1L )
1069 return RESULT_STATE;
1071 int write_size = write(m_Handle, buf, buf_len);
1073 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1074 return RESULT_WRITEFAIL;
1076 *bytes_written = write_size;
1083 //------------------------------------------------------------------------------------------
1088 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1091 ui32_t read_size = 0;
1095 Result_t result = File.OpenRead(filename);
1097 if ( KM_SUCCESS(result) )
1099 fsize = File.Size();
1101 if ( fsize > (Kumu::fpos_t)max_size )
1103 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1104 return RESULT_ALLOC;
1109 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1110 return RESULT_READFAIL;
1113 result = ReadBuf.Capacity((ui32_t)fsize);
1116 if ( KM_SUCCESS(result) )
1117 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1119 if ( KM_SUCCESS(result) )
1120 outString.assign((const char*)ReadBuf.RoData(), read_size);
1128 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1131 ui32_t write_count = 0;
1133 Result_t result = File.OpenWrite(filename);
1135 if ( KM_SUCCESS(result) )
1136 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1141 //------------------------------------------------------------------------------------------
1146 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1149 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1150 Result_t result = Buffer.Capacity(file_size);
1152 if ( KM_SUCCESS(result) )
1154 ui32_t read_count = 0;
1157 result = Reader.OpenRead(Filename);
1159 if ( KM_SUCCESS(result) )
1160 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1162 if ( KM_SUCCESS(result) )
1164 assert(file_size == read_count);
1165 Buffer.Length(read_count);
1166 MemIOReader MemReader(&Buffer);
1167 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1176 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1179 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1181 if ( KM_SUCCESS(result) )
1183 ui32_t write_count = 0;
1185 MemIOWriter MemWriter(&Buffer);
1187 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1189 if ( KM_SUCCESS(result) )
1191 Buffer.Length(MemWriter.Length());
1192 result = Writer.OpenWrite(Filename);
1195 if ( KM_SUCCESS(result) )
1196 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1202 //------------------------------------------------------------------------------------------
1207 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1209 ui32_t file_size = FileSize(Filename);
1210 Result_t result = Buffer.Capacity(file_size);
1212 if ( KM_SUCCESS(result) )
1214 ui32_t read_count = 0;
1217 result = Reader.OpenRead(Filename);
1219 if ( KM_SUCCESS(result) )
1220 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1222 if ( KM_SUCCESS(result) )
1224 if ( file_size != read_count)
1225 return RESULT_READFAIL;
1227 Buffer.Length(read_count);
1236 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1238 ui32_t write_count = 0;
1241 Result_t result = Writer.OpenWrite(Filename);
1243 if ( KM_SUCCESS(result) )
1244 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1246 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1247 return RESULT_WRITEFAIL;
1252 //------------------------------------------------------------------------------------------
1256 // Win32 directory scanner
1261 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1266 Kumu::DirScanner::Open(const std::string& filename)
1268 // we need to append a '*' to read the entire directory
1269 ui32_t fn_len = filename.size();
1270 char* tmp_file = (char*)malloc(fn_len + 8);
1272 if ( tmp_file == 0 )
1273 return RESULT_ALLOC;
1275 strcpy(tmp_file, filename.c_str());
1276 char* p = &tmp_file[fn_len] - 1;
1278 if ( *p != '/' && *p != '\\' )
1288 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1289 Result_t result = RESULT_OK;
1291 if ( m_Handle == -1 )
1292 result = RESULT_NOT_FOUND;
1301 Kumu::DirScanner::Close()
1303 if ( m_Handle == -1 )
1304 return RESULT_FILEOPEN;
1306 if ( _findclose((long)m_Handle) == -1 )
1314 // This sets filename param to the same per-instance buffer every time, so
1315 // the value will change on the next call
1317 Kumu::DirScanner::GetNext(char* filename)
1319 KM_TEST_NULL_L(filename);
1321 if ( m_Handle == -1 )
1322 return RESULT_FILEOPEN;
1324 if ( m_FileInfo.name[0] == '\0' )
1325 return RESULT_ENDOFFILE;
1327 strncpy(filename, m_FileInfo.name, MaxFilePath);
1328 Result_t result = RESULT_OK;
1330 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1332 m_FileInfo.name[0] = '\0';
1334 if ( errno != ENOENT )
1335 result = RESULT_FAIL;
1344 // POSIX directory scanner
1347 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1351 Kumu::DirScanner::Open(const std::string& dirname)
1353 Result_t result = RESULT_OK;
1355 if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1361 result = RESULT_NOTAFILE;
1363 result = RESULT_NO_PERM;
1366 result = RESULT_PARAM;
1369 result = RESULT_STATE;
1371 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1372 result = RESULT_FAIL;
1382 Kumu::DirScanner::Close()
1384 if ( m_Handle == NULL )
1385 return RESULT_FILEOPEN;
1387 if ( closedir(m_Handle) == -1 ) {
1392 return RESULT_STATE;
1394 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1406 Kumu::DirScanner::GetNext(char* filename)
1408 KM_TEST_NULL_L(filename);
1410 if ( m_Handle == NULL )
1411 return RESULT_FILEOPEN;
1413 struct dirent* entry;
1417 if ( ( entry = readdir(m_Handle)) == NULL )
1418 return RESULT_ENDOFFILE;
1423 strncpy(filename, entry->d_name, MaxFilePath);
1429 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1433 Kumu::DirScannerEx::Open(const std::string& dirname)
1435 Result_t result = RESULT_OK;
1437 if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1443 result = RESULT_NOTAFILE;
1445 result = RESULT_NO_PERM;
1448 result = RESULT_PARAM;
1451 result = RESULT_STATE;
1453 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1454 result = RESULT_FAIL;
1458 if ( KM_SUCCESS(result) )
1459 m_Dirname = dirname;
1461 KM_RESULT_STATE_TEST_IMPLICIT();
1467 Kumu::DirScannerEx::Close()
1469 if ( m_Handle == NULL )
1470 return RESULT_FILEOPEN;
1472 if ( closedir(m_Handle) == -1 )
1478 KM_RESULT_STATE_HERE();
1479 return RESULT_STATE;
1482 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1493 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1495 if ( m_Handle == 0 )
1496 return RESULT_FILEOPEN;
1498 struct dirent* entry;
1502 if ( ( entry = readdir(m_Handle) ) == 0 )
1503 return RESULT_ENDOFFILE;
1508 next_item_name.assign(entry->d_name, strlen(entry->d_name));
1510 switch ( entry->d_type )
1513 next_item_type = DET_DIR;
1517 next_item_type = DET_FILE;
1521 next_item_type = DET_LINK;
1525 next_item_type = DET_DEV;
1535 //------------------------------------------------------------------------------------------
1538 // Attention Windows users: make sure to use the proper separator character
1539 // with these functions.
1542 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1545 Kumu::CreateDirectoriesInPath(const std::string& Path)
1547 bool abs = PathIsAbsolute(Path);
1548 PathCompList_t PathComps, TmpPathComps;
1550 PathToComponents(Path, PathComps);
1552 while ( ! PathComps.empty() )
1554 TmpPathComps.push_back(PathComps.front());
1555 PathComps.pop_front();
1556 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1558 if ( ! PathIsDirectory(tmp_path) )
1561 if ( _mkdir(tmp_path.c_str()) != 0 )
1563 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1566 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1567 tmp_path.c_str(), strerror(errno));
1568 return RESULT_DIR_CREATE;
1579 Kumu::DeleteFile(const std::string& filename)
1581 if ( _unlink(filename.c_str()) == 0 )
1587 case ENOTDIR: return RESULT_NOTAFILE;
1592 case EPERM: return RESULT_NO_PERM;
1595 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1601 h__DeletePath(const std::string& pathname)
1603 if ( pathname.empty() )
1604 return RESULT_NULL_STR;
1606 Result_t result = RESULT_OK;
1608 if ( ! PathIsDirectory(pathname) )
1610 result = DeleteFile(pathname);
1616 char next_file[Kumu::MaxFilePath];
1617 result = TestDir.Open(pathname.c_str());
1619 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1621 if ( next_file[0] == '.' )
1623 if ( next_file[1] == 0 )
1624 continue; // don't delete 'this'
1626 if ( next_file[1] == '.' && next_file[2] == 0 )
1627 continue; // don't delete 'this' parent
1630 result = h__DeletePath(pathname + std::string("/") + next_file);
1634 if ( _rmdir(pathname.c_str()) != 0 )
1640 result = RESULT_NOTAFILE;
1647 result = RESULT_NO_PERM;
1651 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1652 result = RESULT_FAIL;
1662 Kumu::DeletePath(const std::string& pathname)
1664 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1665 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1666 return h__DeletePath(c_pathname);
1672 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1674 DirScanner source_dir;
1675 char next_file[Kumu::MaxFilePath];
1677 Result_t result = source_dir.Open(path);
1679 if ( KM_FAILURE(result) )
1682 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1684 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1685 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1688 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1691 return DeletePath(path);
1695 //------------------------------------------------------------------------------------------
1700 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1703 ULARGE_INTEGER lTotalNumberOfBytes;
1704 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1706 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1709 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1710 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1714 HRESULT last_error = ::GetLastError();
1716 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1721 if ( statfs(path.c_str(), &s) == 0 )
1723 if ( s.f_blocks < 1 )
1725 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1726 path.c_str(), s.f_blocks);
1730 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1731 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1738 case ENOTDIR: return RESULT_NOTAFILE;
1739 case EACCES: return RESULT_NO_PERM;
1742 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1749 // end KM_fileio.cpp