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>
54 typedef struct _stati64 fstat_t;
58 // win32 has WriteFileGather() and ReadFileScatter() but they
59 // demand page alignment and page sizing, making them unsuitable
60 // for use with arbitrary buffer sizes.
62 char* iov_base; // stupid iovec uses char*
66 # if defined(__linux__)
67 # include <sys/statfs.h>
69 # include <sys/param.h>
70 # include <sys/mount.h>
75 typedef struct stat fstat_t;
78 #if defined(__sun) && defined(__SVR4)
79 #include <sys/statfs.h>
84 do_stat(const char* path, fstat_t* stat_info)
86 KM_TEST_NULL_STR_L(path);
87 KM_TEST_NULL_L(stat_info);
89 Kumu::Result_t result = Kumu::RESULT_OK;
92 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
94 if ( _stati64(path, stat_info) == (__int64)-1 )
95 result = Kumu::RESULT_FILEOPEN;
97 ::SetErrorMode( prev );
99 if ( stat(path, stat_info) == -1L )
100 result = Kumu::RESULT_FILEOPEN;
102 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
103 result = Kumu::RESULT_FILEOPEN;
112 static Kumu::Result_t
113 do_fstat(FileHandle handle, fstat_t* stat_info)
115 KM_TEST_NULL_L(stat_info);
117 Kumu::Result_t result = Kumu::RESULT_OK;
119 if ( fstat(handle, stat_info) == -1L )
120 result = Kumu::RESULT_FILEOPEN;
122 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
123 result = Kumu::RESULT_FILEOPEN;
133 Kumu::PathExists(const std::string& pathname)
135 if ( pathname.empty() )
140 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
148 Kumu::PathIsFile(const std::string& pathname)
150 if ( pathname.empty() )
155 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
157 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
167 Kumu::PathIsDirectory(const std::string& pathname)
169 if ( pathname.empty() )
174 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
176 if ( info.st_mode & S_IFDIR )
185 Kumu::FileSize(const std::string& pathname)
187 if ( pathname.empty() )
192 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
194 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
195 return(info.st_size);
203 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
205 PathCompList_t::const_iterator i;
206 for ( i = in_list.begin(); i != in_list.end(); ++i )
210 if ( ! out_list.empty() )
215 else if ( *i != "." )
217 out_list.push_back(*i);
224 Kumu::PathMakeCanonical(const std::string& Path, char separator)
226 PathCompList_t in_list, out_list;
227 bool is_absolute = PathIsAbsolute(Path, separator);
228 PathToComponents(Path, in_list, separator);
229 make_canonical_list(in_list, out_list);
232 return ComponentsToAbsolutePath(out_list, separator);
234 return ComponentsToPath(out_list, separator);
239 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
241 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
245 Kumu::PathCompList_t&
246 Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
250 PathCompList_t tmp_list = km_token_split(path, std::string(s));
251 PathCompList_t::const_iterator i;
253 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
257 component_list.push_back(*i);
261 return component_list;
266 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
271 PathCompList_t::const_iterator ci = CList.begin();
272 std::string out_path = *ci;
274 for ( ci++; ci != CList.end(); ci++ )
275 out_path += separator + *ci;
282 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
284 std::string out_path;
287 out_path = separator;
290 PathCompList_t::const_iterator ci;
292 for ( ci = CList.begin(); ci != CList.end(); ci++ )
293 out_path += separator + *ci;
301 Kumu::PathHasComponents(const std::string& Path, char separator)
303 if ( strchr(Path.c_str(), separator) == 0 )
311 Kumu::PathIsAbsolute(const std::string& Path, char separator)
316 if ( Path[0] == separator)
326 char cwd_buf [MaxFilePath];
327 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
329 DefaultLogSink().Error("Error retrieving current working directory.");
338 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
347 if ( PathIsAbsolute(Path, separator) )
348 return PathMakeCanonical(Path);
350 PathCompList_t in_list, out_list;
351 PathToComponents(PathJoin(PathCwd(), Path), in_list);
352 make_canonical_list(in_list, out_list);
354 return ComponentsToAbsolutePath(out_list);
359 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
361 size_t pos = Path.find(Parent);
363 if ( pos == 0 ) // Parent found at offset 0
364 return Path.substr(Parent.size()+1);
371 Kumu::PathBasename(const std::string& Path, char separator)
373 PathCompList_t CList;
374 PathToComponents(Path, CList, separator);
384 Kumu::PathDirname(const std::string& Path, char separator)
386 PathCompList_t CList;
387 bool is_absolute = PathIsAbsolute(Path, separator);
388 PathToComponents(Path, CList, separator);
391 return is_absolute ? "/" : "";
396 return ComponentsToAbsolutePath(CList, separator);
398 return ComponentsToPath(CList, separator);
403 Kumu::PathGetExtension(const std::string& Path)
405 std::string Basename = PathBasename(Path);
406 const char* p = strrchr(Basename.c_str(), '.');
416 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
418 std::string Basename = PathBasename(Path);
419 const char* p = strrchr(Basename.c_str(), '.');
422 Basename = Basename.substr(0, p - Basename.c_str());
424 if ( Extension.empty() )
427 return Basename + "." + Extension;
432 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
434 return Path1 + separator + Path2;
439 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
441 return Path1 + separator + Path2 + separator + Path3;
446 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
447 const std::string& Path3, const std::string& Path4, char separator)
449 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
453 // returns false if link cannot be read
456 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
458 PathCompList_t in_list, out_list;
459 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
460 PathCompList_t::iterator i;
461 char link_buf[MaxFilePath];
463 for ( i = in_list.begin(); i != in_list.end(); ++i )
465 assert ( *i != ".." && *i != "." );
466 out_list.push_back(*i);
470 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
471 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
473 if ( link_size == -1 )
475 if ( errno == EINVAL )
478 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
482 assert(link_size < MaxFilePath);
483 link_buf[link_size] = 0;
484 std::string tmp_path;
487 if ( PathIsAbsolute(link_buf) )
493 tmp_path = PathJoin(PathDirname(next_link), link_buf);
496 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
500 resolved_path = ComponentsToAbsolutePath(out_list, separator);
505 // TODO: is there a reasonable equivalent to be written for win32?
508 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
510 resolved_path = link_path;
517 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
518 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
520 PathList_t::const_iterator si;
521 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
523 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
525 if ( one_shot && ! FoundPaths.empty() )
534 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
535 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
537 char name_buf[MaxFilePath];
540 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
542 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
544 if ( name_buf[0] == '.' ) continue; // no hidden files
545 std::string tmp_path = SearchDir + separator + name_buf;
547 if ( PathIsDirectory(tmp_path.c_str()) )
548 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
550 else if ( Pattern.Match(name_buf) )
552 FoundPaths.push_back(SearchDir + separator + name_buf);
566 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
568 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
573 regerror(result, &m_regex, buf, 128);
574 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
579 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
580 m_regex = rhs.m_regex;
583 Kumu::PathMatchRegex::~PathMatchRegex() {
588 Kumu::PathMatchRegex::Match(const std::string& s) const {
589 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
595 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
597 std::string regex; // convert glob to regex
599 for ( const char* p = glob.c_str(); *p != 0; p++ )
603 case '.': regex += "\\."; break;
604 case '*': regex += ".*"; break;
605 case '?': regex += ".?"; break;
606 default: regex += *p;
611 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
616 regerror(result, &m_regex, buf, 128);
617 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
622 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
623 m_regex = rhs.m_regex;
626 Kumu::PathMatchGlob::~PathMatchGlob() {
631 Kumu::PathMatchGlob::Match(const std::string& s) const {
632 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
638 //------------------------------------------------------------------------------------------
640 #define X_BUFSIZE 1024
644 Kumu::GetExecutablePath(const std::string& default_path)
646 char path[X_BUFSIZE] = {0};
647 bool success = false;
649 #if defined(KM_WIN32)
650 DWORD size = X_BUFSIZE;
651 DWORD rc = GetModuleFileName(0, path, size);
652 success = ( rc != 0 );
653 #elif defined(KM_MACOSX)
654 uint32_t size = X_BUFSIZE;
655 int rc = _NSGetExecutablePath(path, &size);
656 success = ( rc != -1 );
657 #elif defined(__linux__)
658 size_t size = X_BUFSIZE;
659 ssize_t rc = readlink("/proc/self/exe", path, size);
660 success = ( rc != -1 );
661 #elif defined(__OpenBSD__) || defined(__FreeBSD__)
662 size_t size = X_BUFSIZE;
663 ssize_t rc = readlink("/proc/curproc/file", path, size);
664 success = ( rc != -1 );
665 #elif defined(__FreeBSD__)
666 size_t size = X_BUFSIZE;
667 ssize_t rc = readlink("/proc/curproc/file", path, size);
668 success = ( rc != -1 );
669 #elif defined(__NetBSD__)
670 size_t size = X_BUFSIZE;
671 ssize_t rc = readlink("/proc/curproc/file", path, size);
672 success = ( rc != -1 );
673 #elif defined(__sun) && defined(__SVR4)
674 size_t size = X_BUFSIZE;
675 char program[MAXPATHLEN];
676 snprintf(program, MAXPATHLEN, "/proc/%d/path/a.out", getpid());
677 ssize_t rc = readlink(program, path, size);
679 #error GetExecutablePath --> Create a method for obtaining the executable name
684 return Kumu::PathMakeCanonical(path);
691 //------------------------------------------------------------------------------------------
692 // portable aspects of the file classes
694 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
697 class Kumu::FileWriter::h__iovec
701 struct iovec m_iovec[IOVecMaxEntries];
702 h__iovec() : m_Count(0) {}
709 Kumu::FileReader::Size() const
712 return FileSize(m_Filename.c_str());
716 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
718 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
719 return(info.st_size);
726 // these are declared here instead of in the header file
727 // because we have a mem_ptr that is managing a hidden class
728 Kumu::FileWriter::FileWriter() {}
729 Kumu::FileWriter::~FileWriter() {}
733 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
735 assert( ! m_IOVec.empty() );
736 register h__iovec* iov = m_IOVec;
739 if ( iov->m_Count >= IOVecMaxEntries )
741 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
743 return RESULT_WRITEFAIL;
746 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
747 iov->m_iovec[iov->m_Count].iov_len = buf_len;
755 //------------------------------------------------------------------------------------------
759 Kumu::FileReader::OpenRead(const std::string& filename) const
761 const_cast<FileReader*>(this)->m_Filename = filename;
763 // suppress popup window on error
764 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
766 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename.c_str(),
767 (GENERIC_READ), // open for reading
768 FILE_SHARE_READ, // share for reading
770 OPEN_EXISTING, // read
771 FILE_ATTRIBUTE_NORMAL, // normal file
772 NULL // no template file
775 ::SetErrorMode(prev);
777 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
778 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
783 Kumu::FileReader::Close() const
785 if ( m_Handle == INVALID_HANDLE_VALUE )
786 return Kumu::RESULT_FILEOPEN;
788 // suppress popup window on error
789 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
790 BOOL result = ::CloseHandle(m_Handle);
791 ::SetErrorMode(prev);
792 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
794 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
799 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
801 if ( m_Handle == INVALID_HANDLE_VALUE )
802 return Kumu::RESULT_STATE;
805 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
806 in.QuadPart = position;
807 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
808 HRESULT LastError = GetLastError();
809 ::SetErrorMode(prev);
811 if ( (LastError != NO_ERROR
812 && (in.LowPart == INVALID_SET_FILE_POINTER
813 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
814 return Kumu::RESULT_READFAIL;
816 return Kumu::RESULT_OK;
821 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
825 if ( m_Handle == INVALID_HANDLE_VALUE )
826 return Kumu::RESULT_FILEOPEN;
829 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
830 in.QuadPart = (__int64)0;
831 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
832 HRESULT LastError = GetLastError();
833 ::SetErrorMode(prev);
835 if ( (LastError != NO_ERROR
836 && (in.LowPart == INVALID_SET_FILE_POINTER
837 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
838 return Kumu::RESULT_READFAIL;
840 *pos = (Kumu::fpos_t)in.QuadPart;
841 return Kumu::RESULT_OK;
846 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
849 Result_t result = Kumu::RESULT_OK;
853 if ( read_count == 0 )
854 read_count = &tmp_int;
858 if ( m_Handle == INVALID_HANDLE_VALUE )
859 return Kumu::RESULT_FILEOPEN;
861 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
862 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
863 result = Kumu::RESULT_READFAIL;
865 ::SetErrorMode(prev);
867 if ( tmp_count == 0 ) /* EOF */
868 result = Kumu::RESULT_ENDOFFILE;
870 if ( KM_SUCCESS(result) )
871 *read_count = tmp_count;
878 //------------------------------------------------------------------------------------------
883 Kumu::FileWriter::OpenWrite(const std::string& filename)
885 m_Filename = filename;
887 // suppress popup window on error
888 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
890 m_Handle = ::CreateFileA(filename.c_str(),
891 (GENERIC_WRITE|GENERIC_READ), // open for reading
892 FILE_SHARE_READ, // share for reading
894 CREATE_ALWAYS, // overwrite (beware!)
895 FILE_ATTRIBUTE_NORMAL, // normal file
896 NULL // no template file
899 ::SetErrorMode(prev);
901 if ( m_Handle == INVALID_HANDLE_VALUE )
902 return Kumu::RESULT_FILEOPEN;
904 m_IOVec = new h__iovec;
905 return Kumu::RESULT_OK;
910 Kumu::FileWriter::Writev(ui32_t* bytes_written)
912 assert( ! m_IOVec.empty() );
913 register h__iovec* iov = m_IOVec;
916 if ( bytes_written == 0 )
917 bytes_written = &tmp_int;
919 if ( m_Handle == INVALID_HANDLE_VALUE )
920 return Kumu::RESULT_STATE;
923 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
924 Result_t result = Kumu::RESULT_OK;
926 // AFAIK, there is no writev() equivalent in the win32 API
927 for ( register int i = 0; i < iov->m_Count; i++ )
929 ui32_t tmp_count = 0;
930 BOOL wr_result = ::WriteFile(m_Handle,
931 iov->m_iovec[i].iov_base,
932 iov->m_iovec[i].iov_len,
936 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
938 result = Kumu::RESULT_WRITEFAIL;
942 *bytes_written += tmp_count;
945 ::SetErrorMode(prev);
946 iov->m_Count = 0; // error nor not, all is lost
953 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
958 if ( bytes_written == 0 )
959 bytes_written = &tmp_int;
961 if ( m_Handle == INVALID_HANDLE_VALUE )
962 return Kumu::RESULT_STATE;
964 // suppress popup window on error
965 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
966 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
967 ::SetErrorMode(prev);
969 if ( result == 0 || *bytes_written != buf_len )
970 return Kumu::RESULT_WRITEFAIL;
972 return Kumu::RESULT_OK;
976 //------------------------------------------------------------------------------------------
981 Kumu::FileReader::OpenRead(const std::string& filename) const
983 const_cast<FileReader*>(this)->m_Filename = filename;
984 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
985 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
990 Kumu::FileReader::Close() const
992 if ( m_Handle == -1L )
993 return RESULT_FILEOPEN;
996 const_cast<FileReader*>(this)->m_Handle = -1L;
1002 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1004 if ( m_Handle == -1L )
1005 return RESULT_FILEOPEN;
1007 if ( lseek(m_Handle, position, whence) == -1L )
1008 return RESULT_BADSEEK;
1015 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1017 KM_TEST_NULL_L(pos);
1019 if ( m_Handle == -1L )
1020 return RESULT_FILEOPEN;
1022 Kumu::fpos_t tmp_pos;
1024 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1025 return RESULT_READFAIL;
1033 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1035 KM_TEST_NULL_L(buf);
1036 i32_t tmp_count = 0;
1039 if ( read_count == 0 )
1040 read_count = &tmp_int;
1044 if ( m_Handle == -1L )
1045 return RESULT_FILEOPEN;
1047 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1048 return RESULT_READFAIL;
1050 *read_count = tmp_count;
1051 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1055 //------------------------------------------------------------------------------------------
1060 Kumu::FileWriter::OpenWrite(const std::string& filename)
1062 m_Filename = filename;
1063 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0666);
1065 if ( m_Handle == -1L )
1067 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1068 return RESULT_FILEOPEN;
1071 m_IOVec = new h__iovec;
1077 Kumu::FileWriter::OpenModify(const std::string& filename)
1079 m_Filename = filename;
1080 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0666);
1082 if ( m_Handle == -1L )
1084 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1085 return RESULT_FILEOPEN;
1088 m_IOVec = new h__iovec;
1094 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1096 assert( ! m_IOVec.empty() );
1097 register h__iovec* iov = m_IOVec;
1100 if ( bytes_written == 0 )
1101 bytes_written = &tmp_int;
1103 if ( m_Handle == -1L )
1104 return RESULT_STATE;
1107 for ( int i = 0; i < iov->m_Count; i++ )
1108 total_size += iov->m_iovec[i].iov_len;
1110 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1112 if ( write_size == -1L || write_size != total_size )
1113 return RESULT_WRITEFAIL;
1116 *bytes_written = write_size;
1122 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1124 KM_TEST_NULL_L(buf);
1127 if ( bytes_written == 0 )
1128 bytes_written = &tmp_int;
1130 if ( m_Handle == -1L )
1131 return RESULT_STATE;
1133 int write_size = write(m_Handle, buf, buf_len);
1135 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1136 return RESULT_WRITEFAIL;
1138 *bytes_written = write_size;
1145 //------------------------------------------------------------------------------------------
1150 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1153 ui32_t read_size = 0;
1157 Result_t result = File.OpenRead(filename);
1159 if ( KM_SUCCESS(result) )
1161 fsize = File.Size();
1163 if ( fsize > (Kumu::fpos_t)max_size )
1165 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1166 return RESULT_ALLOC;
1171 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1172 return RESULT_READFAIL;
1175 result = ReadBuf.Capacity((ui32_t)fsize);
1178 if ( KM_SUCCESS(result) )
1179 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1181 if ( KM_SUCCESS(result) )
1182 outString.assign((const char*)ReadBuf.RoData(), read_size);
1190 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1193 ui32_t write_count = 0;
1195 Result_t result = File.OpenWrite(filename);
1197 if ( KM_SUCCESS(result) )
1198 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1203 //------------------------------------------------------------------------------------------
1208 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1211 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1212 Result_t result = Buffer.Capacity(file_size);
1214 if ( KM_SUCCESS(result) )
1216 ui32_t read_count = 0;
1219 result = Reader.OpenRead(Filename);
1221 if ( KM_SUCCESS(result) )
1222 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1224 if ( KM_SUCCESS(result) )
1226 assert(file_size == read_count);
1227 Buffer.Length(read_count);
1228 MemIOReader MemReader(&Buffer);
1229 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1238 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1241 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1243 if ( KM_SUCCESS(result) )
1245 ui32_t write_count = 0;
1247 MemIOWriter MemWriter(&Buffer);
1249 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1251 if ( KM_SUCCESS(result) )
1253 Buffer.Length(MemWriter.Length());
1254 result = Writer.OpenWrite(Filename);
1257 if ( KM_SUCCESS(result) )
1258 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1264 //------------------------------------------------------------------------------------------
1269 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1271 ui32_t file_size = FileSize(Filename);
1272 Result_t result = Buffer.Capacity(file_size);
1274 if ( KM_SUCCESS(result) )
1276 ui32_t read_count = 0;
1279 result = Reader.OpenRead(Filename);
1281 if ( KM_SUCCESS(result) )
1282 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1284 if ( KM_SUCCESS(result) )
1286 if ( file_size != read_count)
1287 return RESULT_READFAIL;
1289 Buffer.Length(read_count);
1298 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1300 ui32_t write_count = 0;
1303 Result_t result = Writer.OpenWrite(Filename);
1305 if ( KM_SUCCESS(result) )
1306 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1308 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1309 return RESULT_WRITEFAIL;
1314 //------------------------------------------------------------------------------------------
1318 // Win32 directory scanner
1323 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1328 Kumu::DirScanner::Open(const std::string& filename)
1330 // we need to append a '*' to read the entire directory
1331 ui32_t fn_len = filename.size();
1332 char* tmp_file = (char*)malloc(fn_len + 8);
1334 if ( tmp_file == 0 )
1335 return RESULT_ALLOC;
1337 strcpy(tmp_file, filename.c_str());
1338 char* p = &tmp_file[fn_len] - 1;
1340 if ( *p != '/' && *p != '\\' )
1350 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1351 Result_t result = RESULT_OK;
1353 if ( m_Handle == -1 )
1354 result = RESULT_NOT_FOUND;
1363 Kumu::DirScanner::Close()
1365 if ( m_Handle == -1 )
1366 return RESULT_FILEOPEN;
1368 if ( _findclose((long)m_Handle) == -1 )
1376 // This sets filename param to the same per-instance buffer every time, so
1377 // the value will change on the next call
1379 Kumu::DirScanner::GetNext(char* filename)
1381 KM_TEST_NULL_L(filename);
1383 if ( m_Handle == -1 )
1384 return RESULT_FILEOPEN;
1386 if ( m_FileInfo.name[0] == '\0' )
1387 return RESULT_ENDOFFILE;
1389 strncpy(filename, m_FileInfo.name, MaxFilePath);
1390 Result_t result = RESULT_OK;
1392 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1394 m_FileInfo.name[0] = '\0';
1396 if ( errno != ENOENT )
1397 result = RESULT_FAIL;
1405 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1409 Kumu::DirScannerEx::Open(const std::string& dirname)
1411 Kumu::DefaultLogSink().Critical("Kumu::DirScannerEx unimplemented for Win32 API.\n");
1412 return RESULT_NOTIMPL;
1417 Kumu::DirScannerEx::Close()
1419 Kumu::DefaultLogSink().Critical("Kumu::DirScannerEx unimplemented for Win32 API.\n");
1420 return RESULT_NOTIMPL;
1425 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1427 Kumu::DefaultLogSink().Critical("Kumu::DirScannerEx unimplemented for Win32 API.\n");
1428 return RESULT_NOTIMPL;
1433 // POSIX directory scanner
1436 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1440 Kumu::DirScanner::Open(const std::string& dirname)
1442 Result_t result = RESULT_OK;
1444 if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1450 result = RESULT_NOTAFILE;
1452 result = RESULT_NO_PERM;
1455 result = RESULT_PARAM;
1458 result = RESULT_STATE;
1460 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1461 result = RESULT_FAIL;
1471 Kumu::DirScanner::Close()
1473 if ( m_Handle == NULL )
1474 return RESULT_FILEOPEN;
1476 if ( closedir(m_Handle) == -1 ) {
1481 return RESULT_STATE;
1483 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1495 Kumu::DirScanner::GetNext(char* filename)
1497 KM_TEST_NULL_L(filename);
1499 if ( m_Handle == NULL )
1500 return RESULT_FILEOPEN;
1502 struct dirent* entry;
1506 if ( ( entry = readdir(m_Handle)) == NULL )
1507 return RESULT_ENDOFFILE;
1512 strncpy(filename, entry->d_name, MaxFilePath);
1516 //------------------------------------------------------------------------------------------
1519 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1523 Kumu::DirScannerEx::Open(const std::string& dirname)
1525 Result_t result = RESULT_OK;
1527 if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1533 result = RESULT_NOTAFILE;
1535 result = RESULT_NO_PERM;
1538 result = RESULT_PARAM;
1541 result = RESULT_STATE;
1543 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1544 result = RESULT_FAIL;
1548 if ( KM_SUCCESS(result) )
1549 m_Dirname = dirname;
1551 KM_RESULT_STATE_TEST_IMPLICIT();
1557 Kumu::DirScannerEx::Close()
1559 if ( m_Handle == NULL )
1560 return RESULT_FILEOPEN;
1562 if ( closedir(m_Handle) == -1 )
1568 KM_RESULT_STATE_HERE();
1569 return RESULT_STATE;
1572 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1583 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1585 if ( m_Handle == 0 )
1586 return RESULT_FILEOPEN;
1588 #if defined(__sun) && defined(__SVR4)
1591 struct dirent* entry;
1595 if ( ( entry = readdir(m_Handle) ) == 0 )
1596 return RESULT_ENDOFFILE;
1601 next_item_name.assign(entry->d_name, strlen(entry->d_name));
1603 #if defined(__sun) && defined(__SVR4)
1605 stat(entry->d_name, &s);
1607 switch ( s.st_mode )
1610 next_item_type = DET_DIR;
1614 next_item_type = DET_FILE;
1618 next_item_type = DET_LINK;
1622 next_item_type = DET_DEV;
1625 switch ( entry->d_type )
1628 next_item_type = DET_DIR;
1632 next_item_type = DET_FILE;
1636 next_item_type = DET_LINK;
1640 next_item_type = DET_DEV;
1649 //------------------------------------------------------------------------------------------
1652 // Attention Windows users: make sure to use the proper separator character
1653 // with these functions.
1656 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1659 Kumu::CreateDirectoriesInPath(const std::string& Path)
1661 bool abs = PathIsAbsolute(Path);
1662 PathCompList_t PathComps, TmpPathComps;
1664 PathToComponents(Path, PathComps);
1666 while ( ! PathComps.empty() )
1668 TmpPathComps.push_back(PathComps.front());
1669 PathComps.pop_front();
1670 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1672 if ( ! PathIsDirectory(tmp_path) )
1675 if ( _mkdir(tmp_path.c_str()) != 0 )
1677 if ( mkdir(tmp_path.c_str(), 0777) != 0 )
1680 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1681 tmp_path.c_str(), strerror(errno));
1682 return RESULT_DIR_CREATE;
1693 Kumu::DeleteFile(const std::string& filename)
1695 if ( _unlink(filename.c_str()) == 0 )
1701 case ENOTDIR: return RESULT_NOTAFILE;
1706 case EPERM: return RESULT_NO_PERM;
1709 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1715 h__DeletePath(const std::string& pathname)
1717 if ( pathname.empty() )
1718 return RESULT_NULL_STR;
1720 Result_t result = RESULT_OK;
1722 if ( ! PathIsDirectory(pathname) )
1724 result = DeleteFile(pathname);
1730 char next_file[Kumu::MaxFilePath];
1731 result = TestDir.Open(pathname.c_str());
1733 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1735 if ( next_file[0] == '.' )
1737 if ( next_file[1] == 0 )
1738 continue; // don't delete 'this'
1740 if ( next_file[1] == '.' && next_file[2] == 0 )
1741 continue; // don't delete 'this' parent
1744 result = h__DeletePath(pathname + std::string("/") + next_file);
1748 if ( _rmdir(pathname.c_str()) != 0 )
1754 result = RESULT_NOTAFILE;
1761 result = RESULT_NO_PERM;
1765 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1766 result = RESULT_FAIL;
1776 Kumu::DeletePath(const std::string& pathname)
1778 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1779 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1780 return h__DeletePath(c_pathname);
1786 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1788 DirScanner source_dir;
1789 char next_file[Kumu::MaxFilePath];
1791 Result_t result = source_dir.Open(path);
1793 if ( KM_FAILURE(result) )
1796 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1798 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1799 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1802 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1805 return DeletePath(path);
1809 //------------------------------------------------------------------------------------------
1814 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1817 ULARGE_INTEGER lTotalNumberOfBytes;
1818 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1820 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1823 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1824 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1828 HRESULT last_error = ::GetLastError();
1830 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1835 #if defined(__sun) && defined(__SVR4)
1836 if ( statfs(path.c_str(), &s, s.f_bsize, s.f_fstyp ) == 0 )
1837 { if ( s.f_blocks < 1 )
1839 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1840 path.c_str(), s.f_blocks);
1844 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bfree;
1845 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1849 if ( statfs(path.c_str(), &s) == 0 )
1851 if ( s.f_blocks < 1 )
1853 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1854 path.c_str(), s.f_blocks);
1858 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1859 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1866 case ENOTDIR: return RESULT_NOTAFILE;
1867 case EACCES: return RESULT_NO_PERM;
1870 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1878 // end KM_fileio.cpp