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;
768 Kumu::wbstr_to_utf8(const Kumu::ByteString& in, std::string& out)
771 assert(in.Length()%sizeof(wchar_t)==0);
772 const wchar_t* p = (const wchar_t*)in.RoData();
774 int stringLength = static_cast<int>( in.Length() );
775 int len = WideCharToMultiByte(CP_UTF8, 0, p, stringLength, NULL, 0, NULL, NULL);
776 char *mb_buf = new char[len];
777 WideCharToMultiByte(CP_UTF8, 0, p, stringLength, mb_buf, len, NULL, NULL);
785 Kumu::utf8_to_wbstr(const std::string& in, Kumu::ByteString& out)
787 Result_t result = out.Capacity((in.size()+1)*sizeof(wchar_t));
789 if ( KM_FAILURE(result) )
794 assert(in.size()*sizeof(wchar_t)<=out.Capacity());
795 const char* read_pos = in.c_str();
796 wchar_t character, *write_pos = (wchar_t*)out.Data();
798 int stringLength = static_cast<int>( in.length() ) + 1;
799 int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, 0, 0);
800 result = out.Capacity(len*sizeof(wchar_t));
801 if ( KM_FAILURE(result) )
805 MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, write_pos, len);
806 out.Length(len*sizeof(wchar_t));
814 //------------------------------------------------------------------------------------------
818 Kumu::FileReader::OpenRead(const std::string& filename) const
820 const_cast<FileReader*>(this)->m_Filename = filename;
821 ByteString wb_filename;
822 Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
824 if ( KM_FAILURE(result) )
829 // suppress popup window on error
830 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
832 const_cast<FileReader*>(this)->m_Handle =
833 ::CreateFileW((wchar_t*)wb_filename.RoData(),
834 (GENERIC_READ), // open for reading
835 FILE_SHARE_READ, // share for reading
837 OPEN_EXISTING, // read
838 FILE_ATTRIBUTE_NORMAL, // normal file
839 NULL // no template file
842 ::SetErrorMode(prev);
844 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
845 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
850 Kumu::FileReader::Close() const
852 if ( m_Handle == INVALID_HANDLE_VALUE )
853 return Kumu::RESULT_FILEOPEN;
855 // suppress popup window on error
856 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
857 BOOL result = ::CloseHandle(m_Handle);
858 ::SetErrorMode(prev);
859 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
861 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
866 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
868 if ( m_Handle == INVALID_HANDLE_VALUE )
869 return Kumu::RESULT_STATE;
872 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
873 in.QuadPart = position;
874 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
875 HRESULT LastError = GetLastError();
876 ::SetErrorMode(prev);
878 if ( (LastError != NO_ERROR
879 && (in.LowPart == INVALID_SET_FILE_POINTER
880 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
881 return Kumu::RESULT_READFAIL;
883 return Kumu::RESULT_OK;
888 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
892 if ( m_Handle == INVALID_HANDLE_VALUE )
893 return Kumu::RESULT_FILEOPEN;
896 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
897 in.QuadPart = (__int64)0;
898 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
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 *pos = (Kumu::fpos_t)in.QuadPart;
908 return Kumu::RESULT_OK;
913 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
916 Result_t result = Kumu::RESULT_OK;
920 if ( read_count == 0 )
921 read_count = &tmp_int;
925 if ( m_Handle == INVALID_HANDLE_VALUE )
926 return Kumu::RESULT_FILEOPEN;
928 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
929 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
930 result = Kumu::RESULT_READFAIL;
932 ::SetErrorMode(prev);
934 if ( tmp_count == 0 ) /* EOF */
935 result = Kumu::RESULT_ENDOFFILE;
937 if ( KM_SUCCESS(result) )
938 *read_count = tmp_count;
945 //------------------------------------------------------------------------------------------
951 Kumu::FileWriter::OpenWrite(const std::string& filename)
953 m_Filename = filename;
954 ByteString wb_filename;
955 Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
957 if ( KM_FAILURE(result) )
962 // suppress popup window on error
963 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
965 m_Handle = ::CreateFileW((wchar_t*)wb_filename.RoData(),
966 (GENERIC_WRITE|GENERIC_READ), // open for reading
967 FILE_SHARE_READ, // share for reading
969 CREATE_ALWAYS, // overwrite (beware!)
970 FILE_ATTRIBUTE_NORMAL, // normal file
971 NULL // no template file
974 ::SetErrorMode(prev);
976 if ( m_Handle == INVALID_HANDLE_VALUE )
977 return Kumu::RESULT_FILEOPEN;
979 m_IOVec = new h__iovec;
980 return Kumu::RESULT_OK;
985 Kumu::FileWriter::Writev(ui32_t* bytes_written)
987 assert( ! m_IOVec.empty() );
988 register h__iovec* iov = m_IOVec;
991 if ( bytes_written == 0 )
992 bytes_written = &tmp_int;
994 if ( m_Handle == INVALID_HANDLE_VALUE )
995 return Kumu::RESULT_STATE;
998 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
999 Result_t result = Kumu::RESULT_OK;
1001 // AFAIK, there is no writev() equivalent in the win32 API
1002 for ( register int i = 0; i < iov->m_Count; i++ )
1004 ui32_t tmp_count = 0;
1005 BOOL wr_result = ::WriteFile(m_Handle,
1006 iov->m_iovec[i].iov_base,
1007 iov->m_iovec[i].iov_len,
1011 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1013 result = Kumu::RESULT_WRITEFAIL;
1017 *bytes_written += tmp_count;
1020 ::SetErrorMode(prev);
1021 iov->m_Count = 0; // error nor not, all is lost
1028 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1030 KM_TEST_NULL_L(buf);
1033 if ( bytes_written == 0 )
1034 bytes_written = &tmp_int;
1036 if ( m_Handle == INVALID_HANDLE_VALUE )
1037 return Kumu::RESULT_STATE;
1039 // suppress popup window on error
1040 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1041 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1042 ::SetErrorMode(prev);
1044 if ( result == 0 || *bytes_written != buf_len )
1045 return Kumu::RESULT_WRITEFAIL;
1047 return Kumu::RESULT_OK;
1051 //------------------------------------------------------------------------------------------
1056 Kumu::FileReader::OpenRead(const std::string& filename) const
1058 const_cast<FileReader*>(this)->m_Filename = filename;
1059 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1060 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1065 Kumu::FileReader::Close() const
1067 if ( m_Handle == -1L )
1068 return RESULT_FILEOPEN;
1071 const_cast<FileReader*>(this)->m_Handle = -1L;
1077 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1079 if ( m_Handle == -1L )
1080 return RESULT_FILEOPEN;
1082 if ( lseek(m_Handle, position, whence) == -1L )
1083 return RESULT_BADSEEK;
1090 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1092 KM_TEST_NULL_L(pos);
1094 if ( m_Handle == -1L )
1095 return RESULT_FILEOPEN;
1097 Kumu::fpos_t tmp_pos;
1099 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1100 return RESULT_READFAIL;
1108 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1110 KM_TEST_NULL_L(buf);
1111 i32_t tmp_count = 0;
1114 if ( read_count == 0 )
1115 read_count = &tmp_int;
1119 if ( m_Handle == -1L )
1120 return RESULT_FILEOPEN;
1122 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1123 return RESULT_READFAIL;
1125 *read_count = tmp_count;
1126 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1130 //------------------------------------------------------------------------------------------
1135 Kumu::FileWriter::OpenWrite(const std::string& filename)
1137 m_Filename = filename;
1138 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0666);
1140 if ( m_Handle == -1L )
1142 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1143 return RESULT_FILEOPEN;
1146 m_IOVec = new h__iovec;
1152 Kumu::FileWriter::OpenModify(const std::string& filename)
1154 m_Filename = filename;
1155 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0666);
1157 if ( m_Handle == -1L )
1159 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1160 return RESULT_FILEOPEN;
1163 m_IOVec = new h__iovec;
1169 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1171 assert( ! m_IOVec.empty() );
1172 register h__iovec* iov = m_IOVec;
1175 if ( bytes_written == 0 )
1176 bytes_written = &tmp_int;
1178 if ( m_Handle == -1L )
1179 return RESULT_STATE;
1182 for ( int i = 0; i < iov->m_Count; i++ )
1183 total_size += iov->m_iovec[i].iov_len;
1185 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1187 if ( write_size == -1L || write_size != total_size )
1188 return RESULT_WRITEFAIL;
1191 *bytes_written = write_size;
1197 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1199 KM_TEST_NULL_L(buf);
1202 if ( bytes_written == 0 )
1203 bytes_written = &tmp_int;
1205 if ( m_Handle == -1L )
1206 return RESULT_STATE;
1208 int write_size = write(m_Handle, buf, buf_len);
1210 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1211 return RESULT_WRITEFAIL;
1213 *bytes_written = write_size;
1220 //------------------------------------------------------------------------------------------
1225 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1228 ui32_t read_size = 0;
1232 Result_t result = File.OpenRead(filename);
1234 if ( KM_SUCCESS(result) )
1236 fsize = File.Size();
1238 if ( fsize > (Kumu::fpos_t)max_size )
1240 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1241 return RESULT_ALLOC;
1246 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1247 return RESULT_READFAIL;
1250 result = ReadBuf.Capacity((ui32_t)fsize);
1253 if ( KM_SUCCESS(result) )
1254 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1256 if ( KM_SUCCESS(result) )
1257 outString.assign((const char*)ReadBuf.RoData(), read_size);
1265 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1268 ui32_t write_count = 0;
1270 Result_t result = File.OpenWrite(filename);
1272 if ( KM_SUCCESS(result) )
1273 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1278 //------------------------------------------------------------------------------------------
1283 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1286 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1287 Result_t result = Buffer.Capacity(file_size);
1289 if ( KM_SUCCESS(result) )
1291 ui32_t read_count = 0;
1294 result = Reader.OpenRead(Filename);
1296 if ( KM_SUCCESS(result) )
1297 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1299 if ( KM_SUCCESS(result) )
1301 assert(file_size == read_count);
1302 Buffer.Length(read_count);
1303 MemIOReader MemReader(&Buffer);
1304 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1313 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1316 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1318 if ( KM_SUCCESS(result) )
1320 ui32_t write_count = 0;
1322 MemIOWriter MemWriter(&Buffer);
1324 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1326 if ( KM_SUCCESS(result) )
1328 Buffer.Length(MemWriter.Length());
1329 result = Writer.OpenWrite(Filename);
1332 if ( KM_SUCCESS(result) )
1333 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1339 //------------------------------------------------------------------------------------------
1344 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1346 ui32_t file_size = FileSize(Filename);
1347 Result_t result = Buffer.Capacity(file_size);
1349 if ( KM_SUCCESS(result) )
1351 ui32_t read_count = 0;
1354 result = Reader.OpenRead(Filename);
1356 if ( KM_SUCCESS(result) )
1357 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1359 if ( KM_SUCCESS(result) )
1361 if ( file_size != read_count)
1362 return RESULT_READFAIL;
1364 Buffer.Length(read_count);
1373 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1375 ui32_t write_count = 0;
1378 Result_t result = Writer.OpenWrite(Filename);
1380 if ( KM_SUCCESS(result) )
1381 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1383 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1384 return RESULT_WRITEFAIL;
1389 //------------------------------------------------------------------------------------------
1394 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1398 Kumu::DirScanner::Open(const std::string& dirname)
1400 Result_t result = RESULT_OK;
1402 if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1408 result = RESULT_NOTAFILE;
1410 result = RESULT_NO_PERM;
1413 result = RESULT_PARAM;
1416 result = RESULT_STATE;
1418 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1419 result = RESULT_FAIL;
1429 Kumu::DirScanner::Close()
1431 if ( m_Handle == NULL )
1432 return RESULT_FILEOPEN;
1434 if ( closedir(m_Handle) == -1 ) {
1439 return RESULT_STATE;
1441 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1453 Kumu::DirScanner::GetNext(char* filename)
1455 KM_TEST_NULL_L(filename);
1457 if ( m_Handle == NULL )
1458 return RESULT_FILEOPEN;
1460 struct dirent* entry;
1464 if ( ( entry = readdir(m_Handle)) == NULL )
1465 return RESULT_ENDOFFILE;
1470 strncpy(filename, entry->d_name, MaxFilePath);
1474 //------------------------------------------------------------------------------------------
1477 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1481 Kumu::DirScannerEx::Open(const std::string& dirname)
1483 Result_t result = RESULT_OK;
1485 if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1491 result = RESULT_NOTAFILE;
1493 result = RESULT_NO_PERM;
1496 result = RESULT_PARAM;
1499 result = RESULT_STATE;
1501 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1502 result = RESULT_FAIL;
1506 if ( KM_SUCCESS(result) )
1507 m_Dirname = dirname;
1509 KM_RESULT_STATE_TEST_IMPLICIT();
1515 Kumu::DirScannerEx::Close()
1517 if ( m_Handle == NULL )
1518 return RESULT_FILEOPEN;
1520 if ( closedir(m_Handle) == -1 )
1526 KM_RESULT_STATE_HERE();
1527 return RESULT_STATE;
1530 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1541 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1543 if ( m_Handle == 0 )
1544 return RESULT_FILEOPEN;
1546 #if defined(__sun) && defined(__SVR4)
1549 struct dirent* entry;
1553 if ( ( entry = readdir(m_Handle) ) == 0 )
1554 return RESULT_ENDOFFILE;
1559 next_item_name.assign(entry->d_name, strlen(entry->d_name));
1561 #if defined(__sun) && defined(__SVR4)
1563 stat(entry->d_name, &s);
1565 switch ( s.st_mode )
1568 next_item_type = DET_DIR;
1572 next_item_type = DET_FILE;
1576 next_item_type = DET_LINK;
1580 next_item_type = DET_DEV;
1583 switch ( entry->d_type )
1586 next_item_type = DET_DIR;
1590 next_item_type = DET_FILE;
1594 next_item_type = DET_LINK;
1598 next_item_type = DET_DEV;
1605 //------------------------------------------------------------------------------------------
1608 // Attention Windows users: make sure to use the proper separator character
1609 // with these functions.
1612 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1615 Kumu::CreateDirectoriesInPath(const std::string& Path)
1617 bool abs = PathIsAbsolute(Path);
1618 PathCompList_t PathComps, TmpPathComps;
1620 PathToComponents(Path, PathComps);
1622 while ( ! PathComps.empty() )
1624 TmpPathComps.push_back(PathComps.front());
1625 PathComps.pop_front();
1626 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1628 if ( ! PathIsDirectory(tmp_path) )
1631 if ( _mkdir(tmp_path.c_str()) != 0 )
1633 if ( mkdir(tmp_path.c_str(), 0777) != 0 )
1636 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1637 tmp_path.c_str(), strerror(errno));
1638 return RESULT_DIR_CREATE;
1649 Kumu::DeleteFile(const std::string& filename)
1651 if ( _unlink(filename.c_str()) == 0 )
1657 case ENOTDIR: return RESULT_NOTAFILE;
1662 case EPERM: return RESULT_NO_PERM;
1665 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1671 h__DeletePath(const std::string& pathname)
1673 if ( pathname.empty() )
1674 return RESULT_NULL_STR;
1676 Result_t result = RESULT_OK;
1678 if ( ! PathIsDirectory(pathname) )
1680 result = DeleteFile(pathname);
1686 char next_file[Kumu::MaxFilePath];
1687 result = TestDir.Open(pathname.c_str());
1689 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1691 if ( next_file[0] == '.' )
1693 if ( next_file[1] == 0 )
1694 continue; // don't delete 'this'
1696 if ( next_file[1] == '.' && next_file[2] == 0 )
1697 continue; // don't delete 'this' parent
1700 result = h__DeletePath(pathname + std::string("/") + next_file);
1704 if ( _rmdir(pathname.c_str()) != 0 )
1710 result = RESULT_NOTAFILE;
1717 result = RESULT_NO_PERM;
1721 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1722 result = RESULT_FAIL;
1732 Kumu::DeletePath(const std::string& pathname)
1734 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1735 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1736 return h__DeletePath(c_pathname);
1742 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1744 DirScanner source_dir;
1745 char next_file[Kumu::MaxFilePath];
1747 Result_t result = source_dir.Open(path);
1749 if ( KM_FAILURE(result) )
1752 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1754 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1755 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1758 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1761 return DeletePath(path);
1765 //------------------------------------------------------------------------------------------
1770 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1773 ULARGE_INTEGER lTotalNumberOfBytes;
1774 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1776 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1779 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1780 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1784 HRESULT last_error = ::GetLastError();
1786 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1791 #if defined(__sun) && defined(__SVR4)
1792 if ( statfs(path.c_str(), &s, s.f_bsize, s.f_fstyp ) == 0 )
1793 { if ( s.f_blocks < 1 )
1795 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1796 path.c_str(), s.f_blocks);
1800 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bfree;
1801 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1805 if ( statfs(path.c_str(), &s) == 0 )
1807 if ( s.f_blocks < 1 )
1809 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1810 path.c_str(), s.f_blocks);
1814 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1815 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1822 case ENOTDIR: return RESULT_NOTAFILE;
1823 case EACCES: return RESULT_NO_PERM;
1826 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1834 // end KM_fileio.cpp