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;
765 //------------------------------------------------------------------------------------------
769 Kumu::FileReader::OpenRead(const std::string& filename) const
771 const_cast<FileReader*>(this)->m_Filename = filename;
773 // suppress popup window on error
774 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
776 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename.c_str(),
777 (GENERIC_READ), // open for reading
778 FILE_SHARE_READ, // share for reading
780 OPEN_EXISTING, // read
781 FILE_ATTRIBUTE_NORMAL, // normal file
782 NULL // no template file
785 ::SetErrorMode(prev);
787 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
788 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
793 Kumu::FileReader::Close() const
795 if ( m_Handle == INVALID_HANDLE_VALUE )
796 return Kumu::RESULT_FILEOPEN;
798 // suppress popup window on error
799 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
800 BOOL result = ::CloseHandle(m_Handle);
801 ::SetErrorMode(prev);
802 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
804 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
809 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
811 if ( m_Handle == INVALID_HANDLE_VALUE )
812 return Kumu::RESULT_STATE;
815 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
816 in.QuadPart = position;
817 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
818 HRESULT LastError = GetLastError();
819 ::SetErrorMode(prev);
821 if ( (LastError != NO_ERROR
822 && (in.LowPart == INVALID_SET_FILE_POINTER
823 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
824 return Kumu::RESULT_READFAIL;
826 return Kumu::RESULT_OK;
831 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
835 if ( m_Handle == INVALID_HANDLE_VALUE )
836 return Kumu::RESULT_FILEOPEN;
839 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
840 in.QuadPart = (__int64)0;
841 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
842 HRESULT LastError = GetLastError();
843 ::SetErrorMode(prev);
845 if ( (LastError != NO_ERROR
846 && (in.LowPart == INVALID_SET_FILE_POINTER
847 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
848 return Kumu::RESULT_READFAIL;
850 *pos = (Kumu::fpos_t)in.QuadPart;
851 return Kumu::RESULT_OK;
856 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
859 Result_t result = Kumu::RESULT_OK;
863 if ( read_count == 0 )
864 read_count = &tmp_int;
868 if ( m_Handle == INVALID_HANDLE_VALUE )
869 return Kumu::RESULT_FILEOPEN;
871 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
872 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
873 result = Kumu::RESULT_READFAIL;
875 ::SetErrorMode(prev);
877 if ( tmp_count == 0 ) /* EOF */
878 result = Kumu::RESULT_ENDOFFILE;
880 if ( KM_SUCCESS(result) )
881 *read_count = tmp_count;
888 //------------------------------------------------------------------------------------------
893 Kumu::FileWriter::OpenWrite(const std::string& filename)
895 m_Filename = filename;
897 // suppress popup window on error
898 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
900 m_Handle = ::CreateFileA(filename.c_str(),
901 (GENERIC_WRITE|GENERIC_READ), // open for reading
902 FILE_SHARE_READ, // share for reading
904 CREATE_ALWAYS, // overwrite (beware!)
905 FILE_ATTRIBUTE_NORMAL, // normal file
906 NULL // no template file
909 ::SetErrorMode(prev);
911 if ( m_Handle == INVALID_HANDLE_VALUE )
912 return Kumu::RESULT_FILEOPEN;
914 m_IOVec = new h__iovec;
915 return Kumu::RESULT_OK;
920 Kumu::FileWriter::Writev(ui32_t* bytes_written)
922 assert( ! m_IOVec.empty() );
923 register h__iovec* iov = m_IOVec;
926 if ( bytes_written == 0 )
927 bytes_written = &tmp_int;
929 if ( m_Handle == INVALID_HANDLE_VALUE )
930 return Kumu::RESULT_STATE;
933 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
934 Result_t result = Kumu::RESULT_OK;
936 // AFAIK, there is no writev() equivalent in the win32 API
937 for ( register int i = 0; i < iov->m_Count; i++ )
939 ui32_t tmp_count = 0;
940 BOOL wr_result = ::WriteFile(m_Handle,
941 iov->m_iovec[i].iov_base,
942 iov->m_iovec[i].iov_len,
946 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
948 result = Kumu::RESULT_WRITEFAIL;
952 *bytes_written += tmp_count;
955 ::SetErrorMode(prev);
956 iov->m_Count = 0; // error nor not, all is lost
963 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
968 if ( bytes_written == 0 )
969 bytes_written = &tmp_int;
971 if ( m_Handle == INVALID_HANDLE_VALUE )
972 return Kumu::RESULT_STATE;
974 // suppress popup window on error
975 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
976 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
977 ::SetErrorMode(prev);
979 if ( result == 0 || *bytes_written != buf_len )
980 return Kumu::RESULT_WRITEFAIL;
982 return Kumu::RESULT_OK;
986 //------------------------------------------------------------------------------------------
991 Kumu::FileReader::OpenRead(const std::string& filename) const
993 const_cast<FileReader*>(this)->m_Filename = filename;
994 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
995 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1000 Kumu::FileReader::Close() const
1002 if ( m_Handle == -1L )
1003 return RESULT_FILEOPEN;
1006 const_cast<FileReader*>(this)->m_Handle = -1L;
1012 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1014 if ( m_Handle == -1L )
1015 return RESULT_FILEOPEN;
1017 if ( lseek(m_Handle, position, whence) == -1L )
1018 return RESULT_BADSEEK;
1025 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1027 KM_TEST_NULL_L(pos);
1029 if ( m_Handle == -1L )
1030 return RESULT_FILEOPEN;
1032 Kumu::fpos_t tmp_pos;
1034 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1035 return RESULT_READFAIL;
1043 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1045 KM_TEST_NULL_L(buf);
1046 i32_t tmp_count = 0;
1049 if ( read_count == 0 )
1050 read_count = &tmp_int;
1054 if ( m_Handle == -1L )
1055 return RESULT_FILEOPEN;
1057 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1058 return RESULT_READFAIL;
1060 *read_count = tmp_count;
1061 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1065 //------------------------------------------------------------------------------------------
1070 Kumu::FileWriter::OpenWrite(const std::string& filename)
1072 m_Filename = filename;
1073 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0666);
1075 if ( m_Handle == -1L )
1077 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1078 return RESULT_FILEOPEN;
1081 m_IOVec = new h__iovec;
1087 Kumu::FileWriter::OpenModify(const std::string& filename)
1089 m_Filename = filename;
1090 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0666);
1092 if ( m_Handle == -1L )
1094 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1095 return RESULT_FILEOPEN;
1098 m_IOVec = new h__iovec;
1104 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1106 assert( ! m_IOVec.empty() );
1107 register h__iovec* iov = m_IOVec;
1110 if ( bytes_written == 0 )
1111 bytes_written = &tmp_int;
1113 if ( m_Handle == -1L )
1114 return RESULT_STATE;
1117 for ( int i = 0; i < iov->m_Count; i++ )
1118 total_size += iov->m_iovec[i].iov_len;
1120 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1122 if ( write_size == -1L || write_size != total_size )
1123 return RESULT_WRITEFAIL;
1126 *bytes_written = write_size;
1132 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1134 KM_TEST_NULL_L(buf);
1137 if ( bytes_written == 0 )
1138 bytes_written = &tmp_int;
1140 if ( m_Handle == -1L )
1141 return RESULT_STATE;
1143 int write_size = write(m_Handle, buf, buf_len);
1145 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1146 return RESULT_WRITEFAIL;
1148 *bytes_written = write_size;
1155 //------------------------------------------------------------------------------------------
1160 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1163 ui32_t read_size = 0;
1167 Result_t result = File.OpenRead(filename);
1169 if ( KM_SUCCESS(result) )
1171 fsize = File.Size();
1173 if ( fsize > (Kumu::fpos_t)max_size )
1175 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1176 return RESULT_ALLOC;
1181 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1182 return RESULT_READFAIL;
1185 result = ReadBuf.Capacity((ui32_t)fsize);
1188 if ( KM_SUCCESS(result) )
1189 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1191 if ( KM_SUCCESS(result) )
1192 outString.assign((const char*)ReadBuf.RoData(), read_size);
1200 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1203 ui32_t write_count = 0;
1205 Result_t result = File.OpenWrite(filename);
1207 if ( KM_SUCCESS(result) )
1208 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1213 //------------------------------------------------------------------------------------------
1218 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1221 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1222 Result_t result = Buffer.Capacity(file_size);
1224 if ( KM_SUCCESS(result) )
1226 ui32_t read_count = 0;
1229 result = Reader.OpenRead(Filename);
1231 if ( KM_SUCCESS(result) )
1232 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1234 if ( KM_SUCCESS(result) )
1236 assert(file_size == read_count);
1237 Buffer.Length(read_count);
1238 MemIOReader MemReader(&Buffer);
1239 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1248 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1251 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1253 if ( KM_SUCCESS(result) )
1255 ui32_t write_count = 0;
1257 MemIOWriter MemWriter(&Buffer);
1259 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1261 if ( KM_SUCCESS(result) )
1263 Buffer.Length(MemWriter.Length());
1264 result = Writer.OpenWrite(Filename);
1267 if ( KM_SUCCESS(result) )
1268 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1274 //------------------------------------------------------------------------------------------
1279 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1281 ui32_t file_size = FileSize(Filename);
1282 Result_t result = Buffer.Capacity(file_size);
1284 if ( KM_SUCCESS(result) )
1286 ui32_t read_count = 0;
1289 result = Reader.OpenRead(Filename);
1291 if ( KM_SUCCESS(result) )
1292 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1294 if ( KM_SUCCESS(result) )
1296 if ( file_size != read_count)
1297 return RESULT_READFAIL;
1299 Buffer.Length(read_count);
1308 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1310 ui32_t write_count = 0;
1313 Result_t result = Writer.OpenWrite(Filename);
1315 if ( KM_SUCCESS(result) )
1316 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1318 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1319 return RESULT_WRITEFAIL;
1324 //------------------------------------------------------------------------------------------
1329 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1333 Kumu::DirScanner::Open(const std::string& dirname)
1335 Result_t result = RESULT_OK;
1337 if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1343 result = RESULT_NOTAFILE;
1345 result = RESULT_NO_PERM;
1348 result = RESULT_PARAM;
1351 result = RESULT_STATE;
1353 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1354 result = RESULT_FAIL;
1364 Kumu::DirScanner::Close()
1366 if ( m_Handle == NULL )
1367 return RESULT_FILEOPEN;
1369 if ( closedir(m_Handle) == -1 ) {
1374 return RESULT_STATE;
1376 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1388 Kumu::DirScanner::GetNext(char* filename)
1390 KM_TEST_NULL_L(filename);
1392 if ( m_Handle == NULL )
1393 return RESULT_FILEOPEN;
1395 struct dirent* entry;
1399 if ( ( entry = readdir(m_Handle)) == NULL )
1400 return RESULT_ENDOFFILE;
1405 strncpy(filename, entry->d_name, MaxFilePath);
1409 //------------------------------------------------------------------------------------------
1412 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1416 Kumu::DirScannerEx::Open(const std::string& dirname)
1418 Result_t result = RESULT_OK;
1420 if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1426 result = RESULT_NOTAFILE;
1428 result = RESULT_NO_PERM;
1431 result = RESULT_PARAM;
1434 result = RESULT_STATE;
1436 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1437 result = RESULT_FAIL;
1441 if ( KM_SUCCESS(result) )
1442 m_Dirname = dirname;
1444 KM_RESULT_STATE_TEST_IMPLICIT();
1450 Kumu::DirScannerEx::Close()
1452 if ( m_Handle == NULL )
1453 return RESULT_FILEOPEN;
1455 if ( closedir(m_Handle) == -1 )
1461 KM_RESULT_STATE_HERE();
1462 return RESULT_STATE;
1465 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1476 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1478 if ( m_Handle == 0 )
1479 return RESULT_FILEOPEN;
1481 #if defined(__sun) && defined(__SVR4)
1484 struct dirent* entry;
1488 if ( ( entry = readdir(m_Handle) ) == 0 )
1489 return RESULT_ENDOFFILE;
1494 next_item_name.assign(entry->d_name, strlen(entry->d_name));
1496 #if defined(__sun) && defined(__SVR4)
1498 stat(entry->d_name, &s);
1500 switch ( s.st_mode )
1503 next_item_type = DET_DIR;
1507 next_item_type = DET_FILE;
1511 next_item_type = DET_LINK;
1515 next_item_type = DET_DEV;
1518 switch ( entry->d_type )
1521 next_item_type = DET_DIR;
1525 next_item_type = DET_FILE;
1529 next_item_type = DET_LINK;
1533 next_item_type = DET_DEV;
1540 //------------------------------------------------------------------------------------------
1543 // Attention Windows users: make sure to use the proper separator character
1544 // with these functions.
1547 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1550 Kumu::CreateDirectoriesInPath(const std::string& Path)
1552 bool abs = PathIsAbsolute(Path);
1553 PathCompList_t PathComps, TmpPathComps;
1555 PathToComponents(Path, PathComps);
1557 while ( ! PathComps.empty() )
1559 TmpPathComps.push_back(PathComps.front());
1560 PathComps.pop_front();
1561 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1563 if ( ! PathIsDirectory(tmp_path) )
1566 if ( _mkdir(tmp_path.c_str()) != 0 )
1568 if ( mkdir(tmp_path.c_str(), 0777) != 0 )
1571 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1572 tmp_path.c_str(), strerror(errno));
1573 return RESULT_DIR_CREATE;
1584 Kumu::DeleteFile(const std::string& filename)
1586 if ( _unlink(filename.c_str()) == 0 )
1592 case ENOTDIR: return RESULT_NOTAFILE;
1597 case EPERM: return RESULT_NO_PERM;
1600 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1606 h__DeletePath(const std::string& pathname)
1608 if ( pathname.empty() )
1609 return RESULT_NULL_STR;
1611 Result_t result = RESULT_OK;
1613 if ( ! PathIsDirectory(pathname) )
1615 result = DeleteFile(pathname);
1621 char next_file[Kumu::MaxFilePath];
1622 result = TestDir.Open(pathname.c_str());
1624 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1626 if ( next_file[0] == '.' )
1628 if ( next_file[1] == 0 )
1629 continue; // don't delete 'this'
1631 if ( next_file[1] == '.' && next_file[2] == 0 )
1632 continue; // don't delete 'this' parent
1635 result = h__DeletePath(pathname + std::string("/") + next_file);
1639 if ( _rmdir(pathname.c_str()) != 0 )
1645 result = RESULT_NOTAFILE;
1652 result = RESULT_NO_PERM;
1656 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1657 result = RESULT_FAIL;
1667 Kumu::DeletePath(const std::string& pathname)
1669 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1670 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1671 return h__DeletePath(c_pathname);
1677 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1679 DirScanner source_dir;
1680 char next_file[Kumu::MaxFilePath];
1682 Result_t result = source_dir.Open(path);
1684 if ( KM_FAILURE(result) )
1687 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1689 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1690 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1693 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1696 return DeletePath(path);
1700 //------------------------------------------------------------------------------------------
1705 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1708 ULARGE_INTEGER lTotalNumberOfBytes;
1709 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1711 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1714 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1715 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1719 HRESULT last_error = ::GetLastError();
1721 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1726 #if defined(__sun) && defined(__SVR4)
1727 if ( statfs(path.c_str(), &s, s.f_bsize, s.f_fstyp ) == 0 )
1728 { if ( s.f_blocks < 1 )
1730 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1731 path.c_str(), s.f_blocks);
1735 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bfree;
1736 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1740 if ( statfs(path.c_str(), &s) == 0 )
1742 if ( s.f_blocks < 1 )
1744 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1745 path.c_str(), s.f_blocks);
1749 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1750 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1757 case ENOTDIR: return RESULT_NOTAFILE;
1758 case EACCES: return RESULT_NO_PERM;
1761 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1769 // end KM_fileio.cpp