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
28 \version $Id: KM_fileio.cpp,v 1.40 2015/10/07 16:58:03 jhurst Exp $
29 \brief portable file i/o
32 #include <KM_fileio.h>
41 #define _getcwd getcwd
42 #define _unlink unlink
46 // only needed by GetExecutablePath()
47 #if defined(KM_MACOSX)
48 #include <mach-o/dyld.h>
54 typedef struct _stati64 fstat_t;
58 // win32 has WriteFileGather() and ReadFileScatter() but they
59 // demand page alignment and page sizing, making them unsuitable
60 // for use with arbitrary buffer sizes.
62 char* iov_base; // stupid iovec uses char*
66 # if defined(__linux__)
67 # include <sys/statfs.h>
69 # include <sys/param.h>
70 # include <sys/mount.h>
75 typedef struct stat fstat_t;
81 do_stat(const char* path, fstat_t* stat_info)
83 KM_TEST_NULL_STR_L(path);
84 KM_TEST_NULL_L(stat_info);
86 Kumu::Result_t result = Kumu::RESULT_OK;
89 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
91 if ( _stati64(path, stat_info) == (__int64)-1 )
92 result = Kumu::RESULT_FILEOPEN;
94 ::SetErrorMode( prev );
96 if ( stat(path, stat_info) == -1L )
97 result = Kumu::RESULT_FILEOPEN;
99 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
100 result = Kumu::RESULT_FILEOPEN;
109 static Kumu::Result_t
110 do_fstat(FileHandle handle, fstat_t* stat_info)
112 KM_TEST_NULL_L(stat_info);
114 Kumu::Result_t result = Kumu::RESULT_OK;
116 if ( fstat(handle, stat_info) == -1L )
117 result = Kumu::RESULT_FILEOPEN;
119 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
120 result = Kumu::RESULT_FILEOPEN;
130 Kumu::PathExists(const std::string& pathname)
132 if ( pathname.empty() )
137 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
145 Kumu::PathIsFile(const std::string& pathname)
147 if ( pathname.empty() )
152 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
154 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
164 Kumu::PathIsDirectory(const std::string& pathname)
166 if ( pathname.empty() )
171 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
173 if ( info.st_mode & S_IFDIR )
182 Kumu::FileSize(const std::string& pathname)
184 if ( pathname.empty() )
189 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
191 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
192 return(info.st_size);
200 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
202 PathCompList_t::const_iterator i;
203 for ( i = in_list.begin(); i != in_list.end(); ++i )
207 if ( ! out_list.empty() )
212 else if ( *i != "." )
214 out_list.push_back(*i);
221 Kumu::PathMakeCanonical(const std::string& Path, char separator)
223 PathCompList_t in_list, out_list;
224 bool is_absolute = PathIsAbsolute(Path, separator);
225 PathToComponents(Path, in_list, separator);
226 make_canonical_list(in_list, out_list);
229 return ComponentsToAbsolutePath(out_list, separator);
231 return ComponentsToPath(out_list, separator);
236 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
238 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
242 Kumu::PathCompList_t&
243 Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
247 PathCompList_t tmp_list = km_token_split(path, std::string(s));
248 PathCompList_t::const_iterator i;
250 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
254 component_list.push_back(*i);
258 return component_list;
263 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
268 PathCompList_t::const_iterator ci = CList.begin();
269 std::string out_path = *ci;
271 for ( ci++; ci != CList.end(); ci++ )
272 out_path += separator + *ci;
279 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
281 std::string out_path;
284 out_path = separator;
287 PathCompList_t::const_iterator ci;
289 for ( ci = CList.begin(); ci != CList.end(); ci++ )
290 out_path += separator + *ci;
298 Kumu::PathHasComponents(const std::string& Path, char separator)
300 if ( strchr(Path.c_str(), separator) == 0 )
308 Kumu::PathIsAbsolute(const std::string& Path, char separator)
313 if ( Path[0] == separator)
323 char cwd_buf [MaxFilePath];
324 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
326 DefaultLogSink().Error("Error retrieving current working directory.");
335 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
344 if ( PathIsAbsolute(Path, separator) )
345 return PathMakeCanonical(Path);
347 PathCompList_t in_list, out_list;
348 PathToComponents(PathJoin(PathCwd(), Path), in_list);
349 make_canonical_list(in_list, out_list);
351 return ComponentsToAbsolutePath(out_list);
356 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
358 size_t pos = Path.find(Parent);
360 if ( pos == 0 ) // Parent found at offset 0
361 return Path.substr(Parent.size()+1);
368 Kumu::PathBasename(const std::string& Path, char separator)
370 PathCompList_t CList;
371 PathToComponents(Path, CList, separator);
381 Kumu::PathDirname(const std::string& Path, char separator)
383 PathCompList_t CList;
384 bool is_absolute = PathIsAbsolute(Path, separator);
385 PathToComponents(Path, CList, separator);
388 return is_absolute ? "/" : "";
393 return ComponentsToAbsolutePath(CList, separator);
395 return ComponentsToPath(CList, separator);
400 Kumu::PathGetExtension(const std::string& Path)
402 std::string Basename = PathBasename(Path);
403 const char* p = strrchr(Basename.c_str(), '.');
413 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
415 std::string Basename = PathBasename(Path);
416 const char* p = strrchr(Basename.c_str(), '.');
419 Basename = Basename.substr(0, p - Basename.c_str());
421 if ( Extension.empty() )
424 return Basename + "." + Extension;
429 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
431 return Path1 + separator + Path2;
436 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
438 return Path1 + separator + Path2 + separator + Path3;
443 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
444 const std::string& Path3, const std::string& Path4, char separator)
446 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
450 // returns false if link cannot be read
453 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
455 PathCompList_t in_list, out_list;
456 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
457 PathCompList_t::iterator i;
458 char link_buf[MaxFilePath];
460 for ( i = in_list.begin(); i != in_list.end(); ++i )
462 assert ( *i != ".." && *i != "." );
463 out_list.push_back(*i);
467 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
468 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
470 if ( link_size == -1 )
472 if ( errno == EINVAL )
475 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
479 assert(link_size < MaxFilePath);
480 link_buf[link_size] = 0;
481 std::string tmp_path;
484 if ( PathIsAbsolute(link_buf) )
490 tmp_path = PathJoin(PathDirname(next_link), link_buf);
493 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
497 resolved_path = ComponentsToAbsolutePath(out_list, separator);
502 // TODO: is there a reasonable equivalent to be written for win32?
505 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
507 resolved_path = link_path;
514 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
515 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
517 PathList_t::const_iterator si;
518 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
520 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
522 if ( one_shot && ! FoundPaths.empty() )
531 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
532 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
534 char name_buf[MaxFilePath];
537 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
539 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
541 if ( name_buf[0] == '.' ) continue; // no hidden files
542 std::string tmp_path = SearchDir + separator + name_buf;
544 if ( PathIsDirectory(tmp_path.c_str()) )
545 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
547 else if ( Pattern.Match(name_buf) )
549 FoundPaths.push_back(SearchDir + separator + name_buf);
563 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
565 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
570 regerror(result, &m_regex, buf, 128);
571 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
576 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
577 m_regex = rhs.m_regex;
580 Kumu::PathMatchRegex::~PathMatchRegex() {
585 Kumu::PathMatchRegex::Match(const std::string& s) const {
586 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
592 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
594 std::string regex; // convert glob to regex
596 for ( const char* p = glob.c_str(); *p != 0; p++ )
600 case '.': regex += "\\."; break;
601 case '*': regex += ".*"; break;
602 case '?': regex += ".?"; break;
603 default: regex += *p;
608 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
613 regerror(result, &m_regex, buf, 128);
614 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
619 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
620 m_regex = rhs.m_regex;
623 Kumu::PathMatchGlob::~PathMatchGlob() {
628 Kumu::PathMatchGlob::Match(const std::string& s) const {
629 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
635 //------------------------------------------------------------------------------------------
637 #define X_BUFSIZE 1024
641 Kumu::GetExecutablePath(const std::string& default_path)
643 char path[X_BUFSIZE] = {0};
644 bool success = false;
646 #if defined(KM_WIN32)
647 DWORD size = X_BUFSIZE;
648 DWORD rc = GetModuleFileName(0, path, size);
649 success = ( rc != 0 );
650 #elif defined(KM_MACOSX)
651 uint32_t size = X_BUFSIZE;
652 int rc = _NSGetExecutablePath(path, &size);
653 success = ( rc != -1 );
654 #elif defined(__linux__)
655 size_t size = X_BUFSIZE;
656 ssize_t rc = readlink("/proc/self/exe", path, size);
657 success = ( rc != -1 );
658 #elif defined(__OpenBSD__) || defined(__FreeBSD__)
659 size_t size = X_BUFSIZE;
660 ssize_t rc = readlink("/proc/curproc/file", path, size);
661 success = ( rc != -1 );
662 #elif defined(__FreeBSD__)
663 size_t size = X_BUFSIZE;
664 ssize_t rc = readlink("/proc/curproc/file", path, size);
665 success = ( rc != -1 );
666 #elif defined(__NetBSD__)
667 size_t size = X_BUFSIZE;
668 ssize_t rc = readlink("/proc/curproc/file", path, size);
669 success = ( rc != -1 );
671 #error GetExecutablePath --> Create a method for obtaining the executable name
676 return Kumu::PathMakeCanonical(path);
683 //------------------------------------------------------------------------------------------
684 // portable aspects of the file classes
686 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
689 class Kumu::FileWriter::h__iovec
693 struct iovec m_iovec[IOVecMaxEntries];
694 h__iovec() : m_Count(0) {}
701 Kumu::FileReader::Size() const
704 return FileSize(m_Filename.c_str());
708 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
710 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
711 return(info.st_size);
718 // these are declared here instead of in the header file
719 // because we have a mem_ptr that is managing a hidden class
720 Kumu::FileWriter::FileWriter() {}
721 Kumu::FileWriter::~FileWriter() {}
725 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
727 assert( ! m_IOVec.empty() );
728 register h__iovec* iov = m_IOVec;
731 if ( iov->m_Count >= IOVecMaxEntries )
733 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
735 return RESULT_WRITEFAIL;
738 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
739 iov->m_iovec[iov->m_Count].iov_len = buf_len;
747 //------------------------------------------------------------------------------------------
751 Kumu::FileReader::OpenRead(const std::string& filename) const
753 const_cast<FileReader*>(this)->m_Filename = filename;
755 // suppress popup window on error
756 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
758 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename.c_str(),
759 (GENERIC_READ), // open for reading
760 FILE_SHARE_READ, // share for reading
762 OPEN_EXISTING, // read
763 FILE_ATTRIBUTE_NORMAL, // normal file
764 NULL // no template file
767 ::SetErrorMode(prev);
769 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
770 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
775 Kumu::FileReader::Close() const
777 if ( m_Handle == INVALID_HANDLE_VALUE )
778 return Kumu::RESULT_FILEOPEN;
780 // suppress popup window on error
781 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
782 BOOL result = ::CloseHandle(m_Handle);
783 ::SetErrorMode(prev);
784 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
786 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
791 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
793 if ( m_Handle == INVALID_HANDLE_VALUE )
794 return Kumu::RESULT_STATE;
797 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
798 in.QuadPart = position;
799 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
800 HRESULT LastError = GetLastError();
801 ::SetErrorMode(prev);
803 if ( (LastError != NO_ERROR
804 && (in.LowPart == INVALID_SET_FILE_POINTER
805 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
806 return Kumu::RESULT_READFAIL;
808 return Kumu::RESULT_OK;
813 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
817 if ( m_Handle == INVALID_HANDLE_VALUE )
818 return Kumu::RESULT_FILEOPEN;
821 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
822 in.QuadPart = (__int64)0;
823 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
824 HRESULT LastError = GetLastError();
825 ::SetErrorMode(prev);
827 if ( (LastError != NO_ERROR
828 && (in.LowPart == INVALID_SET_FILE_POINTER
829 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
830 return Kumu::RESULT_READFAIL;
832 *pos = (Kumu::fpos_t)in.QuadPart;
833 return Kumu::RESULT_OK;
838 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
841 Result_t result = Kumu::RESULT_OK;
845 if ( read_count == 0 )
846 read_count = &tmp_int;
850 if ( m_Handle == INVALID_HANDLE_VALUE )
851 return Kumu::RESULT_FILEOPEN;
853 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
854 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
855 result = Kumu::RESULT_READFAIL;
857 ::SetErrorMode(prev);
859 if ( tmp_count == 0 ) /* EOF */
860 result = Kumu::RESULT_ENDOFFILE;
862 if ( KM_SUCCESS(result) )
863 *read_count = tmp_count;
870 //------------------------------------------------------------------------------------------
875 Kumu::FileWriter::OpenWrite(const std::string& filename)
877 m_Filename = filename;
879 // suppress popup window on error
880 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
882 m_Handle = ::CreateFileA(filename.c_str(),
883 (GENERIC_WRITE|GENERIC_READ), // open for reading
884 FILE_SHARE_READ, // share for reading
886 CREATE_ALWAYS, // overwrite (beware!)
887 FILE_ATTRIBUTE_NORMAL, // normal file
888 NULL // no template file
891 ::SetErrorMode(prev);
893 if ( m_Handle == INVALID_HANDLE_VALUE )
894 return Kumu::RESULT_FILEOPEN;
896 m_IOVec = new h__iovec;
897 return Kumu::RESULT_OK;
902 Kumu::FileWriter::Writev(ui32_t* bytes_written)
904 assert( ! m_IOVec.empty() );
905 register h__iovec* iov = m_IOVec;
908 if ( bytes_written == 0 )
909 bytes_written = &tmp_int;
911 if ( m_Handle == INVALID_HANDLE_VALUE )
912 return Kumu::RESULT_STATE;
915 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
916 Result_t result = Kumu::RESULT_OK;
918 // AFAIK, there is no writev() equivalent in the win32 API
919 for ( register int i = 0; i < iov->m_Count; i++ )
921 ui32_t tmp_count = 0;
922 BOOL wr_result = ::WriteFile(m_Handle,
923 iov->m_iovec[i].iov_base,
924 iov->m_iovec[i].iov_len,
928 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
930 result = Kumu::RESULT_WRITEFAIL;
934 *bytes_written += tmp_count;
937 ::SetErrorMode(prev);
938 iov->m_Count = 0; // error nor not, all is lost
945 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
950 if ( bytes_written == 0 )
951 bytes_written = &tmp_int;
953 if ( m_Handle == INVALID_HANDLE_VALUE )
954 return Kumu::RESULT_STATE;
956 // suppress popup window on error
957 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
958 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
959 ::SetErrorMode(prev);
961 if ( result == 0 || *bytes_written != buf_len )
962 return Kumu::RESULT_WRITEFAIL;
964 return Kumu::RESULT_OK;
968 //------------------------------------------------------------------------------------------
973 Kumu::FileReader::OpenRead(const std::string& filename) const
975 const_cast<FileReader*>(this)->m_Filename = filename;
976 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
977 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
982 Kumu::FileReader::Close() const
984 if ( m_Handle == -1L )
985 return RESULT_FILEOPEN;
988 const_cast<FileReader*>(this)->m_Handle = -1L;
994 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
996 if ( m_Handle == -1L )
997 return RESULT_FILEOPEN;
999 if ( lseek(m_Handle, position, whence) == -1L )
1000 return RESULT_BADSEEK;
1007 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1009 KM_TEST_NULL_L(pos);
1011 if ( m_Handle == -1L )
1012 return RESULT_FILEOPEN;
1014 Kumu::fpos_t tmp_pos;
1016 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1017 return RESULT_READFAIL;
1025 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1027 KM_TEST_NULL_L(buf);
1028 i32_t tmp_count = 0;
1031 if ( read_count == 0 )
1032 read_count = &tmp_int;
1036 if ( m_Handle == -1L )
1037 return RESULT_FILEOPEN;
1039 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1040 return RESULT_READFAIL;
1042 *read_count = tmp_count;
1043 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1047 //------------------------------------------------------------------------------------------
1052 Kumu::FileWriter::OpenWrite(const std::string& filename)
1054 m_Filename = filename;
1055 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
1057 if ( m_Handle == -1L )
1059 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1060 return RESULT_FILEOPEN;
1063 m_IOVec = new h__iovec;
1069 Kumu::FileWriter::OpenModify(const std::string& filename)
1071 m_Filename = filename;
1072 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1074 if ( m_Handle == -1L )
1076 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1077 return RESULT_FILEOPEN;
1080 m_IOVec = new h__iovec;
1086 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1088 assert( ! m_IOVec.empty() );
1089 register h__iovec* iov = m_IOVec;
1092 if ( bytes_written == 0 )
1093 bytes_written = &tmp_int;
1095 if ( m_Handle == -1L )
1096 return RESULT_STATE;
1099 for ( int i = 0; i < iov->m_Count; i++ )
1100 total_size += iov->m_iovec[i].iov_len;
1102 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1104 if ( write_size == -1L || write_size != total_size )
1105 return RESULT_WRITEFAIL;
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);
1127 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1128 return RESULT_WRITEFAIL;
1130 *bytes_written = write_size;
1137 //------------------------------------------------------------------------------------------
1142 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1145 ui32_t read_size = 0;
1149 Result_t result = File.OpenRead(filename);
1151 if ( KM_SUCCESS(result) )
1153 fsize = File.Size();
1155 if ( fsize > (Kumu::fpos_t)max_size )
1157 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1158 return RESULT_ALLOC;
1163 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1164 return RESULT_READFAIL;
1167 result = ReadBuf.Capacity((ui32_t)fsize);
1170 if ( KM_SUCCESS(result) )
1171 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1173 if ( KM_SUCCESS(result) )
1174 outString.assign((const char*)ReadBuf.RoData(), read_size);
1182 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1185 ui32_t write_count = 0;
1187 Result_t result = File.OpenWrite(filename);
1189 if ( KM_SUCCESS(result) )
1190 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1195 //------------------------------------------------------------------------------------------
1200 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1203 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1204 Result_t result = Buffer.Capacity(file_size);
1206 if ( KM_SUCCESS(result) )
1208 ui32_t read_count = 0;
1211 result = Reader.OpenRead(Filename);
1213 if ( KM_SUCCESS(result) )
1214 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1216 if ( KM_SUCCESS(result) )
1218 assert(file_size == read_count);
1219 Buffer.Length(read_count);
1220 MemIOReader MemReader(&Buffer);
1221 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1230 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1233 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1235 if ( KM_SUCCESS(result) )
1237 ui32_t write_count = 0;
1239 MemIOWriter MemWriter(&Buffer);
1241 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1243 if ( KM_SUCCESS(result) )
1245 Buffer.Length(MemWriter.Length());
1246 result = Writer.OpenWrite(Filename);
1249 if ( KM_SUCCESS(result) )
1250 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1256 //------------------------------------------------------------------------------------------
1261 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1263 ui32_t file_size = FileSize(Filename);
1264 Result_t result = Buffer.Capacity(file_size);
1266 if ( KM_SUCCESS(result) )
1268 ui32_t read_count = 0;
1271 result = Reader.OpenRead(Filename);
1273 if ( KM_SUCCESS(result) )
1274 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1276 if ( KM_SUCCESS(result) )
1278 if ( file_size != read_count)
1279 return RESULT_READFAIL;
1281 Buffer.Length(read_count);
1290 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1292 ui32_t write_count = 0;
1295 Result_t result = Writer.OpenWrite(Filename);
1297 if ( KM_SUCCESS(result) )
1298 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1300 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1301 return RESULT_WRITEFAIL;
1306 //------------------------------------------------------------------------------------------
1310 // Win32 directory scanner
1315 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1320 Kumu::DirScanner::Open(const std::string& filename)
1322 // we need to append a '*' to read the entire directory
1323 ui32_t fn_len = filename.size();
1324 char* tmp_file = (char*)malloc(fn_len + 8);
1326 if ( tmp_file == 0 )
1327 return RESULT_ALLOC;
1329 strcpy(tmp_file, filename.c_str());
1330 char* p = &tmp_file[fn_len] - 1;
1332 if ( *p != '/' && *p != '\\' )
1342 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1343 Result_t result = RESULT_OK;
1345 if ( m_Handle == -1 )
1346 result = RESULT_NOT_FOUND;
1355 Kumu::DirScanner::Close()
1357 if ( m_Handle == -1 )
1358 return RESULT_FILEOPEN;
1360 if ( _findclose((long)m_Handle) == -1 )
1368 // This sets filename param to the same per-instance buffer every time, so
1369 // the value will change on the next call
1371 Kumu::DirScanner::GetNext(char* filename)
1373 KM_TEST_NULL_L(filename);
1375 if ( m_Handle == -1 )
1376 return RESULT_FILEOPEN;
1378 if ( m_FileInfo.name[0] == '\0' )
1379 return RESULT_ENDOFFILE;
1381 strncpy(filename, m_FileInfo.name, MaxFilePath);
1382 Result_t result = RESULT_OK;
1384 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1386 m_FileInfo.name[0] = '\0';
1388 if ( errno != ENOENT )
1389 result = RESULT_FAIL;
1398 // POSIX directory scanner
1401 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1405 Kumu::DirScanner::Open(const std::string& dirname)
1407 Result_t result = RESULT_OK;
1409 if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1415 result = RESULT_NOTAFILE;
1417 result = RESULT_NO_PERM;
1420 result = RESULT_PARAM;
1423 result = RESULT_STATE;
1425 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1426 result = RESULT_FAIL;
1436 Kumu::DirScanner::Close()
1438 if ( m_Handle == NULL )
1439 return RESULT_FILEOPEN;
1441 if ( closedir(m_Handle) == -1 ) {
1446 return RESULT_STATE;
1448 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1460 Kumu::DirScanner::GetNext(char* filename)
1462 KM_TEST_NULL_L(filename);
1464 if ( m_Handle == NULL )
1465 return RESULT_FILEOPEN;
1467 struct dirent* entry;
1471 if ( ( entry = readdir(m_Handle)) == NULL )
1472 return RESULT_ENDOFFILE;
1477 strncpy(filename, entry->d_name, MaxFilePath);
1483 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1487 Kumu::DirScannerEx::Open(const std::string& dirname)
1489 Result_t result = RESULT_OK;
1491 if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1497 result = RESULT_NOTAFILE;
1499 result = RESULT_NO_PERM;
1502 result = RESULT_PARAM;
1505 result = RESULT_STATE;
1507 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1508 result = RESULT_FAIL;
1512 if ( KM_SUCCESS(result) )
1513 m_Dirname = dirname;
1515 KM_RESULT_STATE_TEST_IMPLICIT();
1521 Kumu::DirScannerEx::Close()
1523 if ( m_Handle == NULL )
1524 return RESULT_FILEOPEN;
1526 if ( closedir(m_Handle) == -1 )
1532 KM_RESULT_STATE_HERE();
1533 return RESULT_STATE;
1536 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1547 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1549 if ( m_Handle == 0 )
1550 return RESULT_FILEOPEN;
1552 struct dirent* entry;
1556 if ( ( entry = readdir(m_Handle) ) == 0 )
1557 return RESULT_ENDOFFILE;
1562 next_item_name.assign(entry->d_name, strlen(entry->d_name));
1564 switch ( entry->d_type )
1567 next_item_type = DET_DIR;
1571 next_item_type = DET_FILE;
1575 next_item_type = DET_LINK;
1579 next_item_type = DET_DEV;
1589 //------------------------------------------------------------------------------------------
1592 // Attention Windows users: make sure to use the proper separator character
1593 // with these functions.
1596 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1599 Kumu::CreateDirectoriesInPath(const std::string& Path)
1601 bool abs = PathIsAbsolute(Path);
1602 PathCompList_t PathComps, TmpPathComps;
1604 PathToComponents(Path, PathComps);
1606 while ( ! PathComps.empty() )
1608 TmpPathComps.push_back(PathComps.front());
1609 PathComps.pop_front();
1610 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1612 if ( ! PathIsDirectory(tmp_path) )
1615 if ( _mkdir(tmp_path.c_str()) != 0 )
1617 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1620 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1621 tmp_path.c_str(), strerror(errno));
1622 return RESULT_DIR_CREATE;
1633 Kumu::DeleteFile(const std::string& filename)
1635 if ( _unlink(filename.c_str()) == 0 )
1641 case ENOTDIR: return RESULT_NOTAFILE;
1646 case EPERM: return RESULT_NO_PERM;
1649 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1655 h__DeletePath(const std::string& pathname)
1657 if ( pathname.empty() )
1658 return RESULT_NULL_STR;
1660 Result_t result = RESULT_OK;
1662 if ( ! PathIsDirectory(pathname) )
1664 result = DeleteFile(pathname);
1670 char next_file[Kumu::MaxFilePath];
1671 result = TestDir.Open(pathname.c_str());
1673 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1675 if ( next_file[0] == '.' )
1677 if ( next_file[1] == 0 )
1678 continue; // don't delete 'this'
1680 if ( next_file[1] == '.' && next_file[2] == 0 )
1681 continue; // don't delete 'this' parent
1684 result = h__DeletePath(pathname + std::string("/") + next_file);
1688 if ( _rmdir(pathname.c_str()) != 0 )
1694 result = RESULT_NOTAFILE;
1701 result = RESULT_NO_PERM;
1705 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1706 result = RESULT_FAIL;
1716 Kumu::DeletePath(const std::string& pathname)
1718 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1719 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1720 return h__DeletePath(c_pathname);
1726 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1728 DirScanner source_dir;
1729 char next_file[Kumu::MaxFilePath];
1731 Result_t result = source_dir.Open(path);
1733 if ( KM_FAILURE(result) )
1736 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1738 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1739 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1742 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1745 return DeletePath(path);
1749 //------------------------------------------------------------------------------------------
1754 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1757 ULARGE_INTEGER lTotalNumberOfBytes;
1758 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1760 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1763 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1764 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1768 HRESULT last_error = ::GetLastError();
1770 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1775 if ( statfs(path.c_str(), &s) == 0 )
1777 if ( s.f_blocks < 1 )
1779 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1780 path.c_str(), s.f_blocks);
1784 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1785 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1792 case ENOTDIR: return RESULT_NOTAFILE;
1793 case EACCES: return RESULT_NO_PERM;
1796 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1803 // end KM_fileio.cpp