2 Copyright (c) 2004-2016, 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
46 // only needed by GetExecutablePath()
47 #if defined(KM_MACOSX)
48 #include <mach-o/dyld.h>
51 #if defined(__OpenBSD__)
52 #include <sys/sysctl.h>
58 typedef struct _stati64 fstat_t;
62 // win32 has WriteFileGather() and ReadFileScatter() but they
63 // demand page alignment and page sizing, making them unsuitable
64 // for use with arbitrary buffer sizes.
66 char* iov_base; // stupid iovec uses char*
70 # if defined(__linux__)
71 # include <sys/statfs.h>
73 # include <sys/param.h>
74 # include <sys/mount.h>
79 typedef struct stat fstat_t;
82 #if defined(__sun) && defined(__SVR4)
83 #include <sys/statfs.h>
88 do_stat(const char* path, fstat_t* stat_info)
90 KM_TEST_NULL_STR_L(path);
91 KM_TEST_NULL_L(stat_info);
93 Kumu::Result_t result = Kumu::RESULT_OK;
96 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
99 ByteString wb_filename;
100 Result_t result = utf8_to_wbstr(path, wb_filename);
101 if ( _wstati64((wchar_t*)wb_filename.RoData(), stat_info) == (__int64)-1 )
103 if ( _stati64(path, stat_info) == (__int64)-1 )
105 result = Kumu::RESULT_FILEOPEN;
107 ::SetErrorMode( prev );
109 if ( stat(path, stat_info) == -1L )
110 result = Kumu::RESULT_FILEOPEN;
112 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
113 result = Kumu::RESULT_FILEOPEN;
122 static Kumu::Result_t
123 do_fstat(FileHandle handle, fstat_t* stat_info)
125 KM_TEST_NULL_L(stat_info);
127 Kumu::Result_t result = Kumu::RESULT_OK;
129 if ( fstat(handle, stat_info) == -1L )
130 result = Kumu::RESULT_FILEOPEN;
132 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
133 result = Kumu::RESULT_FILEOPEN;
143 Kumu::PathExists(const std::string& pathname)
145 if ( pathname.empty() )
150 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
158 Kumu::PathIsFile(const std::string& pathname)
160 if ( pathname.empty() )
165 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
167 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
177 Kumu::PathIsDirectory(const std::string& pathname)
179 if ( pathname.empty() )
184 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
186 if ( info.st_mode & S_IFDIR )
195 Kumu::FileSize(const std::string& pathname)
197 if ( pathname.empty() )
202 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
204 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
205 return(info.st_size);
213 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
215 PathCompList_t::const_iterator i;
216 for ( i = in_list.begin(); i != in_list.end(); ++i )
220 if ( ! out_list.empty() )
225 else if ( *i != "." )
227 out_list.push_back(*i);
234 Kumu::PathMakeCanonical(const std::string& Path, char separator)
236 PathCompList_t in_list, out_list;
237 bool is_absolute = PathIsAbsolute(Path, separator);
238 PathToComponents(Path, in_list, separator);
239 make_canonical_list(in_list, out_list);
242 return ComponentsToAbsolutePath(out_list, separator);
244 return ComponentsToPath(out_list, separator);
249 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
251 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
255 Kumu::PathCompList_t&
256 Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
260 PathCompList_t tmp_list = km_token_split(path, std::string(s));
261 PathCompList_t::const_iterator i;
263 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
267 component_list.push_back(*i);
271 return component_list;
276 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
281 PathCompList_t::const_iterator ci = CList.begin();
282 std::string out_path = *ci;
284 for ( ci++; ci != CList.end(); ci++ )
285 out_path += separator + *ci;
292 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
294 std::string out_path;
297 out_path = separator;
300 PathCompList_t::const_iterator ci;
302 for ( ci = CList.begin(); ci != CList.end(); ci++ )
303 out_path += separator + *ci;
311 Kumu::PathHasComponents(const std::string& Path, char separator)
313 if ( strchr(Path.c_str(), separator) == 0 )
321 Kumu::PathIsAbsolute(const std::string& Path, char separator)
326 if ( Path[0] == separator)
336 char cwd_buf [MaxFilePath];
337 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
339 DefaultLogSink().Error("Error retrieving current working directory.");
348 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
357 if ( PathIsAbsolute(Path, separator) )
358 return PathMakeCanonical(Path);
360 PathCompList_t in_list, out_list;
361 PathToComponents(PathJoin(PathCwd(), Path), in_list);
362 make_canonical_list(in_list, out_list);
364 return ComponentsToAbsolutePath(out_list);
369 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
371 size_t pos = Path.find(Parent);
373 if ( pos == 0 ) // Parent found at offset 0
374 return Path.substr(Parent.size()+1);
381 Kumu::PathBasename(const std::string& Path, char separator)
383 PathCompList_t CList;
384 PathToComponents(Path, CList, separator);
394 Kumu::PathDirname(const std::string& Path, char separator)
396 PathCompList_t CList;
397 bool is_absolute = PathIsAbsolute(Path, separator);
398 PathToComponents(Path, CList, separator);
401 return is_absolute ? "/" : "";
406 return ComponentsToAbsolutePath(CList, separator);
408 return ComponentsToPath(CList, separator);
413 Kumu::PathGetExtension(const std::string& Path)
415 std::string Basename = PathBasename(Path);
416 const char* p = strrchr(Basename.c_str(), '.');
426 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
428 std::string Basename = PathBasename(Path);
429 const char* p = strrchr(Basename.c_str(), '.');
432 Basename = Basename.substr(0, p - Basename.c_str());
434 if ( Extension.empty() )
437 return Basename + "." + Extension;
442 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
444 return Path1 + separator + Path2;
449 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
451 return Path1 + separator + Path2 + separator + Path3;
456 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
457 const std::string& Path3, const std::string& Path4, char separator)
459 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
463 // returns false if link cannot be read
466 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
468 PathCompList_t in_list, out_list;
469 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
470 PathCompList_t::iterator i;
471 char link_buf[MaxFilePath];
473 for ( i = in_list.begin(); i != in_list.end(); ++i )
475 assert ( *i != ".." && *i != "." );
476 out_list.push_back(*i);
480 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
481 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
483 if ( link_size == -1 )
485 if ( errno == EINVAL )
488 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
492 assert(link_size < MaxFilePath);
493 link_buf[link_size] = 0;
494 std::string tmp_path;
497 if ( PathIsAbsolute(link_buf) )
503 tmp_path = PathJoin(PathDirname(next_link), link_buf);
506 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
510 resolved_path = ComponentsToAbsolutePath(out_list, separator);
515 // TODO: is there a reasonable equivalent to be written for win32?
518 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
520 resolved_path = link_path;
527 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
528 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
530 PathList_t::const_iterator si;
531 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
533 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
535 if ( one_shot && ! FoundPaths.empty() )
544 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
545 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
547 char name_buf[MaxFilePath];
550 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
552 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
554 if ( name_buf[0] == '.' ) continue; // no hidden files
555 std::string tmp_path = SearchDir + separator + name_buf;
557 if ( PathIsDirectory(tmp_path.c_str()) )
558 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
560 else if ( Pattern.Match(name_buf) )
562 FoundPaths.push_back(SearchDir + separator + name_buf);
576 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
578 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
583 regerror(result, &m_regex, buf, 128);
584 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
589 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
590 m_regex = rhs.m_regex;
593 Kumu::PathMatchRegex::~PathMatchRegex() {
598 Kumu::PathMatchRegex::Match(const std::string& s) const {
599 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
605 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
607 std::string regex; // convert glob to regex
609 for ( const char* p = glob.c_str(); *p != 0; p++ )
613 case '.': regex += "\\."; break;
614 case '*': regex += ".*"; break;
615 case '?': regex += ".?"; break;
616 default: regex += *p;
621 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
626 regerror(result, &m_regex, buf, 128);
627 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
632 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
633 m_regex = rhs.m_regex;
636 Kumu::PathMatchGlob::~PathMatchGlob() {
641 Kumu::PathMatchGlob::Match(const std::string& s) const {
642 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
648 //------------------------------------------------------------------------------------------
650 #define X_BUFSIZE 1024
654 Kumu::GetExecutablePath(const std::string& default_path)
656 char path[X_BUFSIZE] = {0};
657 bool success = false;
659 #if defined(KM_WIN32)
660 DWORD size = X_BUFSIZE;
661 DWORD rc = GetModuleFileName(0, path, size);
662 success = ( rc != 0 );
663 #elif defined(KM_MACOSX)
664 uint32_t size = X_BUFSIZE;
665 int rc = _NSGetExecutablePath(path, &size);
666 success = ( rc != -1 );
667 #elif defined(__linux__)
668 size_t size = X_BUFSIZE;
669 ssize_t rc = readlink("/proc/self/exe", path, size);
670 success = ( rc != -1 );
671 #elif defined(__OpenBSD__)
672 // This fails if the CWD changes after the program has started but before the
673 // call to GetExecutablePath(). For least surprise, call GetExecutablePath()
674 // immediately in main() and save the value for later use.
675 const char* p = getenv("_");
678 return Kumu::PathMakeAbsolute(p);
680 #elif defined(__FreeBSD__)
682 size_t size = X_BUFSIZE;
683 ssize_t rc = readlink("/proc/curproc/file", path, size);
684 success = ( rc != -1 );
685 #elif defined(__NetBSD__)
686 size_t size = X_BUFSIZE;
687 ssize_t rc = readlink("/proc/curproc/exe", path, size);
688 success = ( rc != -1 );
689 #elif defined(__sun) && defined(__SVR4)
690 size_t size = X_BUFSIZE;
691 char program[MAXPATHLEN];
692 snprintf(program, MAXPATHLEN, "/proc/%d/path/a.out", getpid());
693 ssize_t rc = readlink(program, path, size);
695 #error GetExecutablePath --> Create a method for obtaining the executable name
700 return Kumu::PathMakeCanonical(path);
707 //------------------------------------------------------------------------------------------
708 // portable aspects of the file classes
710 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
713 class Kumu::FileWriter::h__iovec
717 struct iovec m_iovec[IOVecMaxEntries];
718 h__iovec() : m_Count(0) {}
725 Kumu::FileReader::Size() const
729 GetFileSizeEx(m_Handle, &size);
730 return size.QuadPart;
734 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
736 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
737 return(info.st_size);
744 // these are declared here instead of in the header file
745 // because we have a mem_ptr that is managing a hidden class
746 Kumu::FileWriter::FileWriter() {}
747 Kumu::FileWriter::~FileWriter() {}
751 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
753 assert( ! m_IOVec.empty() );
754 register h__iovec* iov = m_IOVec;
757 if ( iov->m_Count >= IOVecMaxEntries )
759 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
761 return RESULT_WRITEFAIL;
764 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
765 iov->m_iovec[iov->m_Count].iov_len = buf_len;
771 Kumu::FileReader::FileReader()
773 m_Handle = INVALID_HANDLE_VALUE;
774 assert(sizeof(off_t) <= sizeof(int64_t));
777 Kumu::FileReader::~FileReader()
779 Kumu::FileReader::Close();
787 Kumu::wbstr_to_utf8(const Kumu::ByteString& in, std::string& out)
790 assert(in.Length()%sizeof(wchar_t)==0);
791 const wchar_t* p = (const wchar_t*)in.RoData();
793 int stringLength = static_cast<int>( in.Length() );
794 int len = WideCharToMultiByte(CP_UTF8, 0, p, stringLength, NULL, 0, NULL, NULL);
795 char *mb_buf = new char[len];
796 WideCharToMultiByte(CP_UTF8, 0, p, stringLength, mb_buf, len, NULL, NULL);
804 Kumu::utf8_to_wbstr(const std::string& in, Kumu::ByteString& out)
806 Result_t result = out.Capacity((in.size()+1)*sizeof(wchar_t));
808 if ( KM_FAILURE(result) )
813 assert(in.size()*sizeof(wchar_t)<=out.Capacity());
814 const char* read_pos = in.c_str();
815 wchar_t character, *write_pos = (wchar_t*)out.Data();
817 int stringLength = static_cast<int>( in.length() ) + 1;
818 int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, 0, 0);
819 result = out.Capacity(len*sizeof(wchar_t));
820 if ( KM_FAILURE(result) )
824 MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, write_pos, len);
825 out.Length(len*sizeof(wchar_t));
830 #endif // KM_WIN32_UTF8
832 //------------------------------------------------------------------------------------------
836 Kumu::FileReader::OpenRead(const std::string& filename) const
838 const_cast<FileReader*>(this)->m_Filename = filename;
840 ByteString wb_filename;
841 Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
843 if ( KM_FAILURE(result) )
849 // suppress popup window on error
850 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
853 const_cast<FileReader*>(this)->m_Handle =
854 ::CreateFileW((wchar_t*)wb_filename.RoData(),
856 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename.c_str(),
858 (GENERIC_READ), // open for reading
859 FILE_SHARE_READ, // share for reading
861 OPEN_EXISTING, // read
862 FILE_ATTRIBUTE_NORMAL, // normal file
863 NULL // no template file
866 ::SetErrorMode(prev);
868 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
869 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
874 Kumu::FileReader::Close() const
876 if ( m_Handle == INVALID_HANDLE_VALUE )
877 return Kumu::RESULT_FILEOPEN;
879 // suppress popup window on error
880 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
881 BOOL result = ::CloseHandle(m_Handle);
882 ::SetErrorMode(prev);
883 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
885 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
890 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
892 if ( m_Handle == INVALID_HANDLE_VALUE )
893 return Kumu::RESULT_STATE;
896 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
897 in.QuadPart = position;
898 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
899 HRESULT LastError = GetLastError();
900 ::SetErrorMode(prev);
902 if ( (LastError != NO_ERROR
903 && (in.LowPart == INVALID_SET_FILE_POINTER
904 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
905 return Kumu::RESULT_READFAIL;
907 return Kumu::RESULT_OK;
912 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
916 if ( m_Handle == INVALID_HANDLE_VALUE )
917 return Kumu::RESULT_FILEOPEN;
920 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
921 in.QuadPart = (__int64)0;
922 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
923 HRESULT LastError = GetLastError();
924 ::SetErrorMode(prev);
926 if ( (LastError != NO_ERROR
927 && (in.LowPart == INVALID_SET_FILE_POINTER
928 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
929 return Kumu::RESULT_READFAIL;
931 *pos = (Kumu::fpos_t)in.QuadPart;
932 return Kumu::RESULT_OK;
937 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
940 Result_t result = Kumu::RESULT_OK;
944 if ( read_count == 0 )
945 read_count = &tmp_int;
949 if ( m_Handle == INVALID_HANDLE_VALUE )
950 return Kumu::RESULT_FILEOPEN;
952 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
953 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
954 result = Kumu::RESULT_READFAIL;
956 ::SetErrorMode(prev);
958 if ( tmp_count == 0 ) /* EOF */
959 result = Kumu::RESULT_ENDOFFILE;
961 if ( KM_SUCCESS(result) )
962 *read_count = tmp_count;
969 //------------------------------------------------------------------------------------------
975 Kumu::FileWriter::OpenWrite(const std::string& filename)
977 m_Filename = filename;
979 ByteString wb_filename;
980 Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
982 if ( KM_FAILURE(result) )
988 // suppress popup window on error
989 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
992 m_Handle = ::CreateFileW((wchar_t*)wb_filename.RoData(),
994 m_Handle = ::CreateFileA(filename.c_str(),
996 (GENERIC_WRITE|GENERIC_READ), // open for reading
997 FILE_SHARE_READ, // share for reading
999 CREATE_ALWAYS, // overwrite (beware!)
1000 FILE_ATTRIBUTE_NORMAL, // normal file
1001 NULL // no template file
1004 ::SetErrorMode(prev);
1006 if ( m_Handle == INVALID_HANDLE_VALUE )
1007 return Kumu::RESULT_FILEOPEN;
1009 m_IOVec = new h__iovec;
1010 return Kumu::RESULT_OK;
1015 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1017 assert( ! m_IOVec.empty() );
1018 register h__iovec* iov = m_IOVec;
1021 if ( bytes_written == 0 )
1022 bytes_written = &tmp_int;
1024 if ( m_Handle == INVALID_HANDLE_VALUE )
1025 return Kumu::RESULT_STATE;
1028 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1029 Result_t result = Kumu::RESULT_OK;
1031 // AFAIK, there is no writev() equivalent in the win32 API
1032 for ( register int i = 0; i < iov->m_Count; i++ )
1034 ui32_t tmp_count = 0;
1035 BOOL wr_result = ::WriteFile(m_Handle,
1036 iov->m_iovec[i].iov_base,
1037 iov->m_iovec[i].iov_len,
1041 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1043 result = Kumu::RESULT_WRITEFAIL;
1047 *bytes_written += tmp_count;
1050 ::SetErrorMode(prev);
1051 iov->m_Count = 0; // error nor not, all is lost
1058 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1060 KM_TEST_NULL_L(buf);
1063 if ( bytes_written == 0 )
1064 bytes_written = &tmp_int;
1066 if ( m_Handle == INVALID_HANDLE_VALUE )
1067 return Kumu::RESULT_STATE;
1069 // suppress popup window on error
1070 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1071 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1072 ::SetErrorMode(prev);
1074 if ( result == 0 || *bytes_written != buf_len )
1075 return Kumu::RESULT_WRITEFAIL;
1077 return Kumu::RESULT_OK;
1081 //------------------------------------------------------------------------------------------
1087 Kumu::FileReader::OpenRead(const std::string& filename) const
1089 const_cast<FileReader*>(this)->m_Filename = filename;
1090 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1091 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1096 Kumu::FileReader::Close() const
1098 if ( m_Handle == -1L )
1099 return RESULT_FILEOPEN;
1102 const_cast<FileReader*>(this)->m_Handle = -1L;
1108 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1110 if ( m_Handle == -1L )
1111 return RESULT_FILEOPEN;
1113 if ( lseek(m_Handle, position, whence) == -1L )
1114 return RESULT_BADSEEK;
1121 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1123 KM_TEST_NULL_L(pos);
1125 if ( m_Handle == -1L )
1126 return RESULT_FILEOPEN;
1128 Kumu::fpos_t tmp_pos;
1130 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1131 return RESULT_READFAIL;
1139 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1141 KM_TEST_NULL_L(buf);
1142 i32_t tmp_count = 0;
1145 if ( read_count == 0 )
1146 read_count = &tmp_int;
1150 if ( m_Handle == -1L )
1151 return RESULT_FILEOPEN;
1153 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1154 return RESULT_READFAIL;
1156 *read_count = tmp_count;
1157 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1160 //------------------------------------------------------------------------------------------
1165 Kumu::FileWriter::OpenWrite(const std::string& filename)
1167 m_Filename = filename;
1168 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0666);
1170 if ( m_Handle == -1L )
1172 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1173 return RESULT_FILEOPEN;
1176 m_IOVec = new h__iovec;
1182 Kumu::FileWriter::OpenModify(const std::string& filename)
1184 m_Filename = filename;
1185 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0666);
1187 if ( m_Handle == -1L )
1189 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1190 return RESULT_FILEOPEN;
1193 m_IOVec = new h__iovec;
1199 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1201 assert( ! m_IOVec.empty() );
1202 register h__iovec* iov = m_IOVec;
1205 if ( bytes_written == 0 )
1206 bytes_written = &tmp_int;
1208 if ( m_Handle == -1L )
1209 return RESULT_STATE;
1212 for ( int i = 0; i < iov->m_Count; i++ )
1213 total_size += iov->m_iovec[i].iov_len;
1215 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1217 if ( write_size == -1L || write_size != total_size )
1218 return RESULT_WRITEFAIL;
1221 *bytes_written = write_size;
1227 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1229 KM_TEST_NULL_L(buf);
1232 if ( bytes_written == 0 )
1233 bytes_written = &tmp_int;
1235 if ( m_Handle == -1L )
1236 return RESULT_STATE;
1238 int write_size = write(m_Handle, buf, buf_len);
1240 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1241 return RESULT_WRITEFAIL;
1243 *bytes_written = write_size;
1250 //------------------------------------------------------------------------------------------
1253 IFileReader* FileReaderFactory::CreateFileReader() const
1255 return new FileReader();
1260 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1263 ui32_t read_size = 0;
1267 Result_t result = File.OpenRead(filename);
1269 if ( KM_SUCCESS(result) )
1271 fsize = File.Size();
1273 if ( fsize > (Kumu::fpos_t)max_size )
1275 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1276 return RESULT_ALLOC;
1285 result = ReadBuf.Capacity((ui32_t)fsize);
1288 if ( KM_SUCCESS(result) )
1289 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1291 if ( KM_SUCCESS(result) )
1292 outString.assign((const char*)ReadBuf.RoData(), read_size);
1300 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1303 ui32_t write_count = 0;
1305 Result_t result = File.OpenWrite(filename);
1307 if ( KM_SUCCESS(result) )
1308 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1313 //------------------------------------------------------------------------------------------
1318 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1321 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1322 Result_t result = Buffer.Capacity(file_size);
1324 if ( KM_SUCCESS(result) )
1326 ui32_t read_count = 0;
1329 result = Reader.OpenRead(Filename);
1331 if ( KM_SUCCESS(result) )
1332 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1334 if ( KM_SUCCESS(result) )
1336 assert(file_size == read_count);
1337 Buffer.Length(read_count);
1338 MemIOReader MemReader(&Buffer);
1339 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1348 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1351 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1353 if ( KM_SUCCESS(result) )
1355 ui32_t write_count = 0;
1357 MemIOWriter MemWriter(&Buffer);
1359 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1361 if ( KM_SUCCESS(result) )
1363 Buffer.Length(MemWriter.Length());
1364 result = Writer.OpenWrite(Filename);
1367 if ( KM_SUCCESS(result) )
1368 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1374 //------------------------------------------------------------------------------------------
1379 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1381 ui32_t file_size = FileSize(Filename);
1382 Result_t result = Buffer.Capacity(file_size);
1384 if ( KM_SUCCESS(result) )
1386 ui32_t read_count = 0;
1389 result = Reader.OpenRead(Filename);
1391 if ( KM_SUCCESS(result) )
1392 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1394 if ( KM_SUCCESS(result) )
1396 if ( file_size != read_count)
1397 return RESULT_READFAIL;
1399 Buffer.Length(read_count);
1408 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1410 ui32_t write_count = 0;
1413 Result_t result = Writer.OpenWrite(Filename);
1415 if ( KM_SUCCESS(result) )
1416 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1418 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1419 return RESULT_WRITEFAIL;
1424 //------------------------------------------------------------------------------------------
1429 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1433 Kumu::DirScanner::Open(const std::string& dirname)
1435 Result_t result = RESULT_OK;
1437 if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1443 result = RESULT_NOTAFILE;
1446 result = RESULT_NO_PERM;
1450 result = RESULT_PARAM;
1454 result = RESULT_STATE;
1457 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1458 result = RESULT_FAIL;
1469 Kumu::DirScanner::Close()
1471 if ( m_Handle == NULL )
1472 return RESULT_FILEOPEN;
1474 if ( closedir(m_Handle) == -1 ) {
1479 return RESULT_STATE;
1481 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1493 Kumu::DirScanner::GetNext(char* filename)
1495 KM_TEST_NULL_L(filename);
1497 if ( m_Handle == NULL )
1498 return RESULT_FILEOPEN;
1500 struct dirent* entry;
1504 if ( ( entry = readdir(m_Handle)) == NULL )
1505 return RESULT_ENDOFFILE;
1510 strncpy(filename, entry->d_name, MaxFilePath);
1514 //------------------------------------------------------------------------------------------
1517 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1521 Kumu::DirScannerEx::Open(const std::string& dirname)
1523 Result_t result = RESULT_OK;
1525 if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1531 result = RESULT_NOTAFILE;
1534 result = RESULT_NO_PERM;
1538 result = RESULT_PARAM;
1542 result = RESULT_STATE;
1545 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1546 result = RESULT_FAIL;
1551 if ( KM_SUCCESS(result) )
1552 m_Dirname = dirname;
1554 KM_RESULT_STATE_TEST_IMPLICIT();
1560 Kumu::DirScannerEx::Close()
1562 if ( m_Handle == NULL )
1563 return RESULT_FILEOPEN;
1565 if ( closedir(m_Handle) == -1 )
1571 KM_RESULT_STATE_HERE();
1572 return RESULT_STATE;
1575 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1586 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1588 if ( m_Handle == 0 )
1589 return RESULT_FILEOPEN;
1591 #if defined(__sun) && defined(__SVR4)
1594 struct dirent* entry;
1598 if ( ( entry = readdir(m_Handle) ) == 0 )
1599 return RESULT_ENDOFFILE;
1604 next_item_name.assign(entry->d_name, strlen(entry->d_name));
1606 #if defined(__sun) && defined(__SVR4)
1608 stat(entry->d_name, &s);
1610 switch ( s.st_mode )
1613 next_item_type = DET_DIR;
1617 next_item_type = DET_FILE;
1621 next_item_type = DET_LINK;
1625 next_item_type = DET_DEV;
1628 switch ( entry->d_type )
1631 next_item_type = DET_DIR;
1635 next_item_type = DET_FILE;
1639 next_item_type = DET_LINK;
1643 next_item_type = DET_DEV;
1650 //------------------------------------------------------------------------------------------
1653 // Attention Windows users: make sure to use the proper separator character
1654 // with these functions.
1657 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1660 Kumu::CreateDirectoriesInPath(const std::string& Path)
1662 bool abs = PathIsAbsolute(Path);
1663 PathCompList_t PathComps, TmpPathComps;
1665 PathToComponents(Path, PathComps);
1667 while ( ! PathComps.empty() )
1669 TmpPathComps.push_back(PathComps.front());
1670 PathComps.pop_front();
1671 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1673 if ( ! PathIsDirectory(tmp_path) )
1676 if ( _mkdir(tmp_path.c_str()) != 0 )
1678 if ( mkdir(tmp_path.c_str(), 0777) != 0 )
1681 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1682 tmp_path.c_str(), strerror(errno));
1683 return RESULT_DIR_CREATE;
1694 Kumu::DeleteFile(const std::string& filename)
1696 if ( _unlink(filename.c_str()) == 0 )
1702 case ENOTDIR: return RESULT_NOTAFILE;
1707 case EPERM: return RESULT_NO_PERM;
1710 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1719 h__DeletePath(const std::string& pathname)
1721 if ( pathname.empty() )
1722 return RESULT_NULL_STR;
1724 Result_t result = RESULT_OK;
1726 if ( ! PathIsDirectory(pathname) )
1728 result = DeleteFile(pathname);
1734 char next_file[Kumu::MaxFilePath];
1735 result = TestDir.Open(pathname.c_str());
1737 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1739 if ( next_file[0] == '.' )
1741 if ( next_file[1] == 0 )
1742 continue; // don't delete 'this'
1744 if ( next_file[1] == '.' && next_file[2] == 0 )
1745 continue; // don't delete 'this' parent
1748 result = h__DeletePath(pathname + std::string("/") + next_file);
1752 if ( _rmdir(pathname.c_str()) != 0 )
1758 result = RESULT_NOTAFILE;
1765 result = RESULT_NO_PERM;
1769 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1770 result = RESULT_FAIL;
1782 Kumu::DeletePath(const std::string& pathname)
1784 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1785 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1786 return h__DeletePath(c_pathname);
1792 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1794 DirScanner source_dir;
1795 char next_file[Kumu::MaxFilePath];
1797 Result_t result = source_dir.Open(path);
1799 if ( KM_FAILURE(result) )
1802 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1804 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1805 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1808 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1811 return DeletePath(path);
1815 //------------------------------------------------------------------------------------------
1820 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1823 ULARGE_INTEGER lTotalNumberOfBytes;
1824 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1826 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1829 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1830 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1834 HRESULT last_error = ::GetLastError();
1836 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1841 #if defined(__sun) && defined(__SVR4)
1842 if ( statfs(path.c_str(), &s, s.f_bsize, s.f_fstyp ) == 0 )
1843 { if ( s.f_blocks < 1 )
1845 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1846 path.c_str(), s.f_blocks);
1850 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bfree;
1851 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1855 if ( statfs(path.c_str(), &s) == 0 )
1857 if ( s.f_blocks < 1 )
1859 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1860 path.c_str(), s.f_blocks);
1864 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1865 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1872 case ENOTDIR: return RESULT_NOTAFILE;
1873 case EACCES: return RESULT_NO_PERM;
1876 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1884 // end KM_fileio.cpp