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);
98 if ( _stati64(path, stat_info) == (__int64)-1 )
99 result = Kumu::RESULT_FILEOPEN;
101 ::SetErrorMode( prev );
103 if ( stat(path, stat_info) == -1L )
104 result = Kumu::RESULT_FILEOPEN;
106 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
107 result = Kumu::RESULT_FILEOPEN;
116 static Kumu::Result_t
117 do_fstat(FileHandle handle, fstat_t* stat_info)
119 KM_TEST_NULL_L(stat_info);
121 Kumu::Result_t result = Kumu::RESULT_OK;
123 if ( fstat(handle, stat_info) == -1L )
124 result = Kumu::RESULT_FILEOPEN;
126 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
127 result = Kumu::RESULT_FILEOPEN;
137 Kumu::PathExists(const std::string& pathname)
139 if ( pathname.empty() )
144 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
152 Kumu::PathIsFile(const std::string& pathname)
154 if ( pathname.empty() )
159 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
161 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
171 Kumu::PathIsDirectory(const std::string& pathname)
173 if ( pathname.empty() )
178 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
180 if ( info.st_mode & S_IFDIR )
189 Kumu::FileSize(const std::string& pathname)
191 if ( pathname.empty() )
196 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
198 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
199 return(info.st_size);
207 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
209 PathCompList_t::const_iterator i;
210 for ( i = in_list.begin(); i != in_list.end(); ++i )
214 if ( ! out_list.empty() )
219 else if ( *i != "." )
221 out_list.push_back(*i);
228 Kumu::PathMakeCanonical(const std::string& Path, char separator)
230 PathCompList_t in_list, out_list;
231 bool is_absolute = PathIsAbsolute(Path, separator);
232 PathToComponents(Path, in_list, separator);
233 make_canonical_list(in_list, out_list);
236 return ComponentsToAbsolutePath(out_list, separator);
238 return ComponentsToPath(out_list, separator);
243 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
245 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
249 Kumu::PathCompList_t&
250 Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
254 PathCompList_t tmp_list = km_token_split(path, std::string(s));
255 PathCompList_t::const_iterator i;
257 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
261 component_list.push_back(*i);
265 return component_list;
270 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
275 PathCompList_t::const_iterator ci = CList.begin();
276 std::string out_path = *ci;
278 for ( ci++; ci != CList.end(); ci++ )
279 out_path += separator + *ci;
286 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
288 std::string out_path;
291 out_path = separator;
294 PathCompList_t::const_iterator ci;
296 for ( ci = CList.begin(); ci != CList.end(); ci++ )
297 out_path += separator + *ci;
305 Kumu::PathHasComponents(const std::string& Path, char separator)
307 if ( strchr(Path.c_str(), separator) == 0 )
315 Kumu::PathIsAbsolute(const std::string& Path, char separator)
320 if ( Path[0] == separator)
330 char cwd_buf [MaxFilePath];
331 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
333 DefaultLogSink().Error("Error retrieving current working directory.");
342 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
351 if ( PathIsAbsolute(Path, separator) )
352 return PathMakeCanonical(Path);
354 PathCompList_t in_list, out_list;
355 PathToComponents(PathJoin(PathCwd(), Path), in_list);
356 make_canonical_list(in_list, out_list);
358 return ComponentsToAbsolutePath(out_list);
363 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
365 size_t pos = Path.find(Parent);
367 if ( pos == 0 ) // Parent found at offset 0
368 return Path.substr(Parent.size()+1);
375 Kumu::PathBasename(const std::string& Path, char separator)
377 PathCompList_t CList;
378 PathToComponents(Path, CList, separator);
388 Kumu::PathDirname(const std::string& Path, char separator)
390 PathCompList_t CList;
391 bool is_absolute = PathIsAbsolute(Path, separator);
392 PathToComponents(Path, CList, separator);
395 return is_absolute ? "/" : "";
400 return ComponentsToAbsolutePath(CList, separator);
402 return ComponentsToPath(CList, separator);
407 Kumu::PathGetExtension(const std::string& Path)
409 std::string Basename = PathBasename(Path);
410 const char* p = strrchr(Basename.c_str(), '.');
420 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
422 std::string Basename = PathBasename(Path);
423 const char* p = strrchr(Basename.c_str(), '.');
426 Basename = Basename.substr(0, p - Basename.c_str());
428 if ( Extension.empty() )
431 return Basename + "." + Extension;
436 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
438 return Path1 + separator + Path2;
443 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
445 return Path1 + separator + Path2 + separator + Path3;
450 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
451 const std::string& Path3, const std::string& Path4, char separator)
453 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
457 // returns false if link cannot be read
460 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
462 PathCompList_t in_list, out_list;
463 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
464 PathCompList_t::iterator i;
465 char link_buf[MaxFilePath];
467 for ( i = in_list.begin(); i != in_list.end(); ++i )
469 assert ( *i != ".." && *i != "." );
470 out_list.push_back(*i);
474 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
475 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
477 if ( link_size == -1 )
479 if ( errno == EINVAL )
482 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
486 assert(link_size < MaxFilePath);
487 link_buf[link_size] = 0;
488 std::string tmp_path;
491 if ( PathIsAbsolute(link_buf) )
497 tmp_path = PathJoin(PathDirname(next_link), link_buf);
500 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
504 resolved_path = ComponentsToAbsolutePath(out_list, separator);
509 // TODO: is there a reasonable equivalent to be written for win32?
512 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
514 resolved_path = link_path;
521 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
522 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
524 PathList_t::const_iterator si;
525 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
527 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
529 if ( one_shot && ! FoundPaths.empty() )
538 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
539 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
541 char name_buf[MaxFilePath];
544 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
546 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
548 if ( name_buf[0] == '.' ) continue; // no hidden files
549 std::string tmp_path = SearchDir + separator + name_buf;
551 if ( PathIsDirectory(tmp_path.c_str()) )
552 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
554 else if ( Pattern.Match(name_buf) )
556 FoundPaths.push_back(SearchDir + separator + name_buf);
570 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
572 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
577 regerror(result, &m_regex, buf, 128);
578 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
583 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
584 m_regex = rhs.m_regex;
587 Kumu::PathMatchRegex::~PathMatchRegex() {
592 Kumu::PathMatchRegex::Match(const std::string& s) const {
593 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
599 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
601 std::string regex; // convert glob to regex
603 for ( const char* p = glob.c_str(); *p != 0; p++ )
607 case '.': regex += "\\."; break;
608 case '*': regex += ".*"; break;
609 case '?': regex += ".?"; break;
610 default: regex += *p;
615 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
620 regerror(result, &m_regex, buf, 128);
621 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
626 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
627 m_regex = rhs.m_regex;
630 Kumu::PathMatchGlob::~PathMatchGlob() {
635 Kumu::PathMatchGlob::Match(const std::string& s) const {
636 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
642 //------------------------------------------------------------------------------------------
644 #define X_BUFSIZE 1024
648 Kumu::GetExecutablePath(const std::string& default_path)
650 char path[X_BUFSIZE] = {0};
651 bool success = false;
653 #if defined(KM_WIN32)
654 DWORD size = X_BUFSIZE;
655 DWORD rc = GetModuleFileName(0, path, size);
656 success = ( rc != 0 );
657 #elif defined(KM_MACOSX)
658 uint32_t size = X_BUFSIZE;
659 int rc = _NSGetExecutablePath(path, &size);
660 success = ( rc != -1 );
661 #elif defined(__linux__)
662 size_t size = X_BUFSIZE;
663 ssize_t rc = readlink("/proc/self/exe", path, size);
664 success = ( rc != -1 );
665 #elif defined(__OpenBSD__)
666 // This fails if the CWD changes after the program has started but before the
667 // call to GetExecutablePath(). For least surprise, call GetExecutablePath()
668 // immediately in main() and save the value for later use.
669 const char* p = getenv("_");
672 return Kumu::PathMakeAbsolute(p);
674 #elif defined(__FreeBSD__)
676 size_t size = X_BUFSIZE;
677 ssize_t rc = readlink("/proc/curproc/file", path, size);
678 success = ( rc != -1 );
679 #elif defined(__NetBSD__)
680 size_t size = X_BUFSIZE;
681 ssize_t rc = readlink("/proc/curproc/exe", path, size);
682 success = ( rc != -1 );
683 #elif defined(__sun) && defined(__SVR4)
684 size_t size = X_BUFSIZE;
685 char program[MAXPATHLEN];
686 snprintf(program, MAXPATHLEN, "/proc/%d/path/a.out", getpid());
687 ssize_t rc = readlink(program, path, size);
689 #error GetExecutablePath --> Create a method for obtaining the executable name
694 return Kumu::PathMakeCanonical(path);
701 //------------------------------------------------------------------------------------------
702 // portable aspects of the file classes
704 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
707 class Kumu::FileWriter::h__iovec
711 struct iovec m_iovec[IOVecMaxEntries];
712 h__iovec() : m_Count(0) {}
719 Kumu::FileReader::Size() const
722 return FileSize(m_Filename.c_str());
726 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
728 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
729 return(info.st_size);
736 // these are declared here instead of in the header file
737 // because we have a mem_ptr that is managing a hidden class
738 Kumu::FileWriter::FileWriter() {}
739 Kumu::FileWriter::~FileWriter() {}
743 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
745 assert( ! m_IOVec.empty() );
746 register h__iovec* iov = m_IOVec;
749 if ( iov->m_Count >= IOVecMaxEntries )
751 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
753 return RESULT_WRITEFAIL;
756 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
757 iov->m_iovec[iov->m_Count].iov_len = buf_len;
769 Kumu::wbstr_to_utf8(const Kumu::ByteString& in, std::string& out)
772 assert(in.Length()%sizeof(wchar_t)==0);
773 const wchar_t* p = (const wchar_t*)in.RoData();
775 int stringLength = static_cast<int>( in.Length() );
776 int len = WideCharToMultiByte(CP_UTF8, 0, p, stringLength, NULL, 0, NULL, NULL);
777 char *mb_buf = new char[len];
778 WideCharToMultiByte(CP_UTF8, 0, p, stringLength, mb_buf, len, NULL, NULL);
786 Kumu::utf8_to_wbstr(const std::string& in, Kumu::ByteString& out)
788 Result_t result = out.Capacity((in.size()+1)*sizeof(wchar_t));
790 if ( KM_FAILURE(result) )
795 assert(in.size()*sizeof(wchar_t)<=out.Capacity());
796 const char* read_pos = in.c_str();
797 wchar_t character, *write_pos = (wchar_t*)out.Data();
799 int stringLength = static_cast<int>( in.length() ) + 1;
800 int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, 0, 0);
801 result = out.Capacity(len*sizeof(wchar_t));
802 if ( KM_FAILURE(result) )
806 MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, write_pos, len);
807 out.Length(len*sizeof(wchar_t));
812 #endif // KM_WIN32_UTF8
814 //------------------------------------------------------------------------------------------
818 Kumu::FileReader::OpenRead(const std::string& filename) const
820 const_cast<FileReader*>(this)->m_Filename = filename;
822 ByteString wb_filename;
823 Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
825 if ( KM_FAILURE(result) )
831 // suppress popup window on error
832 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
835 const_cast<FileReader*>(this)->m_Handle =
836 ::CreateFileW((wchar_t*)wb_filename.RoData(),
838 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename.c_str(),
840 (GENERIC_READ), // open for reading
841 FILE_SHARE_READ, // share for reading
843 OPEN_EXISTING, // read
844 FILE_ATTRIBUTE_NORMAL, // normal file
845 NULL // no template file
848 ::SetErrorMode(prev);
850 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
851 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
856 Kumu::FileReader::Close() const
858 if ( m_Handle == INVALID_HANDLE_VALUE )
859 return Kumu::RESULT_FILEOPEN;
861 // suppress popup window on error
862 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
863 BOOL result = ::CloseHandle(m_Handle);
864 ::SetErrorMode(prev);
865 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
867 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
872 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
874 if ( m_Handle == INVALID_HANDLE_VALUE )
875 return Kumu::RESULT_STATE;
878 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
879 in.QuadPart = position;
880 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
881 HRESULT LastError = GetLastError();
882 ::SetErrorMode(prev);
884 if ( (LastError != NO_ERROR
885 && (in.LowPart == INVALID_SET_FILE_POINTER
886 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
887 return Kumu::RESULT_READFAIL;
889 return Kumu::RESULT_OK;
894 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
898 if ( m_Handle == INVALID_HANDLE_VALUE )
899 return Kumu::RESULT_FILEOPEN;
902 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
903 in.QuadPart = (__int64)0;
904 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
905 HRESULT LastError = GetLastError();
906 ::SetErrorMode(prev);
908 if ( (LastError != NO_ERROR
909 && (in.LowPart == INVALID_SET_FILE_POINTER
910 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
911 return Kumu::RESULT_READFAIL;
913 *pos = (Kumu::fpos_t)in.QuadPart;
914 return Kumu::RESULT_OK;
919 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
922 Result_t result = Kumu::RESULT_OK;
926 if ( read_count == 0 )
927 read_count = &tmp_int;
931 if ( m_Handle == INVALID_HANDLE_VALUE )
932 return Kumu::RESULT_FILEOPEN;
934 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
935 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
936 result = Kumu::RESULT_READFAIL;
938 ::SetErrorMode(prev);
940 if ( tmp_count == 0 ) /* EOF */
941 result = Kumu::RESULT_ENDOFFILE;
943 if ( KM_SUCCESS(result) )
944 *read_count = tmp_count;
951 //------------------------------------------------------------------------------------------
957 Kumu::FileWriter::OpenWrite(const std::string& filename)
959 m_Filename = filename;
961 ByteString wb_filename;
962 Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
964 if ( KM_FAILURE(result) )
970 // suppress popup window on error
971 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
974 m_Handle = ::CreateFileW((wchar_t*)wb_filename.RoData(),
976 m_Handle = ::CreateFileA(filename.c_str(),
978 (GENERIC_WRITE|GENERIC_READ), // open for reading
979 FILE_SHARE_READ, // share for reading
981 CREATE_ALWAYS, // overwrite (beware!)
982 FILE_ATTRIBUTE_NORMAL, // normal file
983 NULL // no template file
986 ::SetErrorMode(prev);
988 if ( m_Handle == INVALID_HANDLE_VALUE )
989 return Kumu::RESULT_FILEOPEN;
991 m_IOVec = new h__iovec;
992 return Kumu::RESULT_OK;
997 Kumu::FileWriter::Writev(ui32_t* bytes_written)
999 assert( ! m_IOVec.empty() );
1000 register h__iovec* iov = m_IOVec;
1003 if ( bytes_written == 0 )
1004 bytes_written = &tmp_int;
1006 if ( m_Handle == INVALID_HANDLE_VALUE )
1007 return Kumu::RESULT_STATE;
1010 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1011 Result_t result = Kumu::RESULT_OK;
1013 // AFAIK, there is no writev() equivalent in the win32 API
1014 for ( register int i = 0; i < iov->m_Count; i++ )
1016 ui32_t tmp_count = 0;
1017 BOOL wr_result = ::WriteFile(m_Handle,
1018 iov->m_iovec[i].iov_base,
1019 iov->m_iovec[i].iov_len,
1023 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1025 result = Kumu::RESULT_WRITEFAIL;
1029 *bytes_written += tmp_count;
1032 ::SetErrorMode(prev);
1033 iov->m_Count = 0; // error nor not, all is lost
1040 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1042 KM_TEST_NULL_L(buf);
1045 if ( bytes_written == 0 )
1046 bytes_written = &tmp_int;
1048 if ( m_Handle == INVALID_HANDLE_VALUE )
1049 return Kumu::RESULT_STATE;
1051 // suppress popup window on error
1052 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1053 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1054 ::SetErrorMode(prev);
1056 if ( result == 0 || *bytes_written != buf_len )
1057 return Kumu::RESULT_WRITEFAIL;
1059 return Kumu::RESULT_OK;
1063 //------------------------------------------------------------------------------------------
1068 Kumu::FileReader::OpenRead(const std::string& filename) const
1070 const_cast<FileReader*>(this)->m_Filename = filename;
1071 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1072 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1077 Kumu::FileReader::Close() const
1079 if ( m_Handle == -1L )
1080 return RESULT_FILEOPEN;
1083 const_cast<FileReader*>(this)->m_Handle = -1L;
1089 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1091 if ( m_Handle == -1L )
1092 return RESULT_FILEOPEN;
1094 if ( lseek(m_Handle, position, whence) == -1L )
1095 return RESULT_BADSEEK;
1102 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1104 KM_TEST_NULL_L(pos);
1106 if ( m_Handle == -1L )
1107 return RESULT_FILEOPEN;
1109 Kumu::fpos_t tmp_pos;
1111 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1112 return RESULT_READFAIL;
1120 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1122 KM_TEST_NULL_L(buf);
1123 i32_t tmp_count = 0;
1126 if ( read_count == 0 )
1127 read_count = &tmp_int;
1131 if ( m_Handle == -1L )
1132 return RESULT_FILEOPEN;
1134 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1135 return RESULT_READFAIL;
1137 *read_count = tmp_count;
1138 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1142 //------------------------------------------------------------------------------------------
1147 Kumu::FileWriter::OpenWrite(const std::string& filename)
1149 m_Filename = filename;
1150 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0666);
1152 if ( m_Handle == -1L )
1154 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1155 return RESULT_FILEOPEN;
1158 m_IOVec = new h__iovec;
1164 Kumu::FileWriter::OpenModify(const std::string& filename)
1166 m_Filename = filename;
1167 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0666);
1169 if ( m_Handle == -1L )
1171 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1172 return RESULT_FILEOPEN;
1175 m_IOVec = new h__iovec;
1181 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1183 assert( ! m_IOVec.empty() );
1184 register h__iovec* iov = m_IOVec;
1187 if ( bytes_written == 0 )
1188 bytes_written = &tmp_int;
1190 if ( m_Handle == -1L )
1191 return RESULT_STATE;
1194 for ( int i = 0; i < iov->m_Count; i++ )
1195 total_size += iov->m_iovec[i].iov_len;
1197 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1199 if ( write_size == -1L || write_size != total_size )
1200 return RESULT_WRITEFAIL;
1203 *bytes_written = write_size;
1209 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1211 KM_TEST_NULL_L(buf);
1214 if ( bytes_written == 0 )
1215 bytes_written = &tmp_int;
1217 if ( m_Handle == -1L )
1218 return RESULT_STATE;
1220 int write_size = write(m_Handle, buf, buf_len);
1222 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1223 return RESULT_WRITEFAIL;
1225 *bytes_written = write_size;
1232 //------------------------------------------------------------------------------------------
1237 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1240 ui32_t read_size = 0;
1244 Result_t result = File.OpenRead(filename);
1246 if ( KM_SUCCESS(result) )
1248 fsize = File.Size();
1250 if ( fsize > (Kumu::fpos_t)max_size )
1252 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1253 return RESULT_ALLOC;
1262 result = ReadBuf.Capacity((ui32_t)fsize);
1265 if ( KM_SUCCESS(result) )
1266 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1268 if ( KM_SUCCESS(result) )
1269 outString.assign((const char*)ReadBuf.RoData(), read_size);
1277 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1280 ui32_t write_count = 0;
1282 Result_t result = File.OpenWrite(filename);
1284 if ( KM_SUCCESS(result) )
1285 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1290 //------------------------------------------------------------------------------------------
1295 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1298 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1299 Result_t result = Buffer.Capacity(file_size);
1301 if ( KM_SUCCESS(result) )
1303 ui32_t read_count = 0;
1306 result = Reader.OpenRead(Filename);
1308 if ( KM_SUCCESS(result) )
1309 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1311 if ( KM_SUCCESS(result) )
1313 assert(file_size == read_count);
1314 Buffer.Length(read_count);
1315 MemIOReader MemReader(&Buffer);
1316 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1325 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1328 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1330 if ( KM_SUCCESS(result) )
1332 ui32_t write_count = 0;
1334 MemIOWriter MemWriter(&Buffer);
1336 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1338 if ( KM_SUCCESS(result) )
1340 Buffer.Length(MemWriter.Length());
1341 result = Writer.OpenWrite(Filename);
1344 if ( KM_SUCCESS(result) )
1345 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1351 //------------------------------------------------------------------------------------------
1356 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1358 ui32_t file_size = FileSize(Filename);
1359 Result_t result = Buffer.Capacity(file_size);
1361 if ( KM_SUCCESS(result) )
1363 ui32_t read_count = 0;
1366 result = Reader.OpenRead(Filename);
1368 if ( KM_SUCCESS(result) )
1369 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1371 if ( KM_SUCCESS(result) )
1373 if ( file_size != read_count)
1374 return RESULT_READFAIL;
1376 Buffer.Length(read_count);
1385 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1387 ui32_t write_count = 0;
1390 Result_t result = Writer.OpenWrite(Filename);
1392 if ( KM_SUCCESS(result) )
1393 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1395 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1396 return RESULT_WRITEFAIL;
1401 //------------------------------------------------------------------------------------------
1406 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1410 Kumu::DirScanner::Open(const std::string& dirname)
1412 Result_t result = RESULT_OK;
1414 if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1420 result = RESULT_NOTAFILE;
1422 result = RESULT_NO_PERM;
1425 result = RESULT_PARAM;
1428 result = RESULT_STATE;
1430 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1431 result = RESULT_FAIL;
1441 Kumu::DirScanner::Close()
1443 if ( m_Handle == NULL )
1444 return RESULT_FILEOPEN;
1446 if ( closedir(m_Handle) == -1 ) {
1451 return RESULT_STATE;
1453 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1465 Kumu::DirScanner::GetNext(char* filename)
1467 KM_TEST_NULL_L(filename);
1469 if ( m_Handle == NULL )
1470 return RESULT_FILEOPEN;
1472 struct dirent* entry;
1476 if ( ( entry = readdir(m_Handle)) == NULL )
1477 return RESULT_ENDOFFILE;
1482 strncpy(filename, entry->d_name, MaxFilePath);
1486 //------------------------------------------------------------------------------------------
1489 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1493 Kumu::DirScannerEx::Open(const std::string& dirname)
1495 Result_t result = RESULT_OK;
1497 if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1503 result = RESULT_NOTAFILE;
1505 result = RESULT_NO_PERM;
1508 result = RESULT_PARAM;
1511 result = RESULT_STATE;
1513 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1514 result = RESULT_FAIL;
1518 if ( KM_SUCCESS(result) )
1519 m_Dirname = dirname;
1521 KM_RESULT_STATE_TEST_IMPLICIT();
1527 Kumu::DirScannerEx::Close()
1529 if ( m_Handle == NULL )
1530 return RESULT_FILEOPEN;
1532 if ( closedir(m_Handle) == -1 )
1538 KM_RESULT_STATE_HERE();
1539 return RESULT_STATE;
1542 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1553 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1555 if ( m_Handle == 0 )
1556 return RESULT_FILEOPEN;
1558 #if defined(__sun) && defined(__SVR4)
1561 struct dirent* entry;
1565 if ( ( entry = readdir(m_Handle) ) == 0 )
1566 return RESULT_ENDOFFILE;
1571 next_item_name.assign(entry->d_name, strlen(entry->d_name));
1573 #if defined(__sun) && defined(__SVR4)
1575 stat(entry->d_name, &s);
1577 switch ( s.st_mode )
1580 next_item_type = DET_DIR;
1584 next_item_type = DET_FILE;
1588 next_item_type = DET_LINK;
1592 next_item_type = DET_DEV;
1595 switch ( entry->d_type )
1598 next_item_type = DET_DIR;
1602 next_item_type = DET_FILE;
1606 next_item_type = DET_LINK;
1610 next_item_type = DET_DEV;
1617 //------------------------------------------------------------------------------------------
1620 // Attention Windows users: make sure to use the proper separator character
1621 // with these functions.
1624 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1627 Kumu::CreateDirectoriesInPath(const std::string& Path)
1629 bool abs = PathIsAbsolute(Path);
1630 PathCompList_t PathComps, TmpPathComps;
1632 PathToComponents(Path, PathComps);
1634 while ( ! PathComps.empty() )
1636 TmpPathComps.push_back(PathComps.front());
1637 PathComps.pop_front();
1638 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1640 if ( ! PathIsDirectory(tmp_path) )
1643 if ( _mkdir(tmp_path.c_str()) != 0 )
1645 if ( mkdir(tmp_path.c_str(), 0777) != 0 )
1648 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1649 tmp_path.c_str(), strerror(errno));
1650 return RESULT_DIR_CREATE;
1661 Kumu::DeleteFile(const std::string& filename)
1663 if ( _unlink(filename.c_str()) == 0 )
1669 case ENOTDIR: return RESULT_NOTAFILE;
1674 case EPERM: return RESULT_NO_PERM;
1677 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1683 h__DeletePath(const std::string& pathname)
1685 if ( pathname.empty() )
1686 return RESULT_NULL_STR;
1688 Result_t result = RESULT_OK;
1690 if ( ! PathIsDirectory(pathname) )
1692 result = DeleteFile(pathname);
1698 char next_file[Kumu::MaxFilePath];
1699 result = TestDir.Open(pathname.c_str());
1701 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1703 if ( next_file[0] == '.' )
1705 if ( next_file[1] == 0 )
1706 continue; // don't delete 'this'
1708 if ( next_file[1] == '.' && next_file[2] == 0 )
1709 continue; // don't delete 'this' parent
1712 result = h__DeletePath(pathname + std::string("/") + next_file);
1716 if ( _rmdir(pathname.c_str()) != 0 )
1722 result = RESULT_NOTAFILE;
1729 result = RESULT_NO_PERM;
1733 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1734 result = RESULT_FAIL;
1744 Kumu::DeletePath(const std::string& pathname)
1746 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1747 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1748 return h__DeletePath(c_pathname);
1754 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1756 DirScanner source_dir;
1757 char next_file[Kumu::MaxFilePath];
1759 Result_t result = source_dir.Open(path);
1761 if ( KM_FAILURE(result) )
1764 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1766 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1767 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1770 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1773 return DeletePath(path);
1777 //------------------------------------------------------------------------------------------
1782 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1785 ULARGE_INTEGER lTotalNumberOfBytes;
1786 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1788 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1791 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1792 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1796 HRESULT last_error = ::GetLastError();
1798 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1803 #if defined(__sun) && defined(__SVR4)
1804 if ( statfs(path.c_str(), &s, s.f_bsize, s.f_fstyp ) == 0 )
1805 { if ( s.f_blocks < 1 )
1807 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1808 path.c_str(), s.f_blocks);
1812 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bfree;
1813 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1817 if ( statfs(path.c_str(), &s) == 0 )
1819 if ( s.f_blocks < 1 )
1821 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1822 path.c_str(), s.f_blocks);
1826 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1827 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1834 case ENOTDIR: return RESULT_NOTAFILE;
1835 case EACCES: return RESULT_NO_PERM;
1838 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1846 // end KM_fileio.cpp