2 Copyright (c) 2004-2016, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file KM_fileio.cpp
29 \brief portable file i/o
32 #include <KM_fileio.h>
41 #define _getcwd getcwd
42 #define _unlink unlink
46 // only needed by GetExecutablePath()
47 #if defined(KM_MACOSX)
48 #include <mach-o/dyld.h>
51 #if defined(__OpenBSD__)
52 #include <sys/sysctl.h>
58 typedef struct _stati64 fstat_t;
62 // win32 has WriteFileGather() and ReadFileScatter() but they
63 // demand page alignment and page sizing, making them unsuitable
64 // for use with arbitrary buffer sizes.
66 char* iov_base; // stupid iovec uses char*
70 # if defined(__linux__)
71 # include <sys/statfs.h>
73 # include <sys/param.h>
74 # include <sys/mount.h>
79 typedef struct stat fstat_t;
82 #if defined(__sun) && defined(__SVR4)
83 #include <sys/statfs.h>
88 do_stat(const char* path, fstat_t* stat_info)
90 KM_TEST_NULL_STR_L(path);
91 KM_TEST_NULL_L(stat_info);
93 Kumu::Result_t result = Kumu::RESULT_OK;
96 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
99 ByteString wb_filename;
100 Result_t result = utf8_to_wbstr(path, wb_filename);
101 if ( _wstati64((wchar_t*)wb_filename.RoData(), stat_info) == (__int64)-1 )
103 if ( _stati64(path, stat_info) == (__int64)-1 )
105 result = Kumu::RESULT_FILEOPEN;
107 ::SetErrorMode( prev );
109 if ( stat(path, stat_info) == -1L )
110 result = Kumu::RESULT_FILEOPEN;
112 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
113 result = Kumu::RESULT_FILEOPEN;
122 static Kumu::Result_t
123 do_fstat(FileHandle handle, fstat_t* stat_info)
125 KM_TEST_NULL_L(stat_info);
127 Kumu::Result_t result = Kumu::RESULT_OK;
129 if ( fstat(handle, stat_info) == -1L )
130 result = Kumu::RESULT_FILEOPEN;
132 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
133 result = Kumu::RESULT_FILEOPEN;
143 Kumu::PathExists(const std::string& pathname)
145 if ( pathname.empty() )
150 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
158 Kumu::PathIsFile(const std::string& pathname)
160 if ( pathname.empty() )
165 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
167 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
177 Kumu::PathIsDirectory(const std::string& pathname)
179 if ( pathname.empty() )
184 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
186 if ( info.st_mode & S_IFDIR )
195 Kumu::FileSize(const std::string& pathname)
197 if ( pathname.empty() )
202 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
204 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
205 return(info.st_size);
213 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
215 PathCompList_t::const_iterator i;
216 for ( i = in_list.begin(); i != in_list.end(); ++i )
220 if ( ! out_list.empty() )
225 else if ( *i != "." )
227 out_list.push_back(*i);
234 Kumu::PathMakeCanonical(const std::string& Path, char separator)
236 PathCompList_t in_list, out_list;
237 bool is_absolute = PathIsAbsolute(Path, separator);
238 PathToComponents(Path, in_list, separator);
239 make_canonical_list(in_list, out_list);
242 return ComponentsToAbsolutePath(out_list, separator);
244 return ComponentsToPath(out_list, separator);
249 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
251 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
255 Kumu::PathCompList_t&
256 Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
260 PathCompList_t tmp_list = km_token_split(path, std::string(s));
261 PathCompList_t::const_iterator i;
263 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
267 component_list.push_back(*i);
271 return component_list;
276 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
281 PathCompList_t::const_iterator ci = CList.begin();
282 std::string out_path = *ci;
284 for ( ci++; ci != CList.end(); ci++ )
285 out_path += separator + *ci;
292 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
294 std::string out_path;
297 out_path = separator;
300 PathCompList_t::const_iterator ci;
302 for ( ci = CList.begin(); ci != CList.end(); ci++ )
303 out_path += separator + *ci;
311 Kumu::PathHasComponents(const std::string& Path, char separator)
313 if ( strchr(Path.c_str(), separator) == 0 )
321 Kumu::PathIsAbsolute(const std::string& Path, char separator)
326 if ( Path[0] == separator)
336 char cwd_buf [MaxFilePath];
337 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
339 DefaultLogSink().Error("Error retrieving current working directory.");
348 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
357 if ( PathIsAbsolute(Path, separator) )
358 return PathMakeCanonical(Path);
360 PathCompList_t in_list, out_list;
361 PathToComponents(PathJoin(PathCwd(), Path), in_list);
362 make_canonical_list(in_list, out_list);
364 return ComponentsToAbsolutePath(out_list);
369 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
371 size_t pos = Path.find(Parent);
373 if ( pos == 0 ) // Parent found at offset 0
374 return Path.substr(Parent.size()+1);
381 Kumu::PathBasename(const std::string& Path, char separator)
383 PathCompList_t CList;
384 PathToComponents(Path, CList, separator);
394 Kumu::PathDirname(const std::string& Path, char separator)
396 PathCompList_t CList;
397 bool is_absolute = PathIsAbsolute(Path, separator);
398 PathToComponents(Path, CList, separator);
401 return is_absolute ? "/" : "";
406 return ComponentsToAbsolutePath(CList, separator);
408 return ComponentsToPath(CList, separator);
413 Kumu::PathGetExtension(const std::string& Path)
415 std::string Basename = PathBasename(Path);
416 const char* p = strrchr(Basename.c_str(), '.');
426 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
428 std::string Basename = PathBasename(Path);
429 const char* p = strrchr(Basename.c_str(), '.');
432 Basename = Basename.substr(0, p - Basename.c_str());
434 if ( Extension.empty() )
437 return Basename + "." + Extension;
442 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
444 return Path1 + separator + Path2;
449 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
451 return Path1 + separator + Path2 + separator + Path3;
456 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
457 const std::string& Path3, const std::string& Path4, char separator)
459 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
463 // returns false if link cannot be read
466 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
468 PathCompList_t in_list, out_list;
469 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
470 PathCompList_t::iterator i;
471 char link_buf[MaxFilePath];
473 for ( i = in_list.begin(); i != in_list.end(); ++i )
475 assert ( *i != ".." && *i != "." );
476 out_list.push_back(*i);
480 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
481 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
483 if ( link_size == -1 )
485 if ( errno == EINVAL )
488 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
492 assert(link_size < MaxFilePath);
493 link_buf[link_size] = 0;
494 std::string tmp_path;
497 if ( PathIsAbsolute(link_buf) )
503 tmp_path = PathJoin(PathDirname(next_link), link_buf);
506 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
510 resolved_path = ComponentsToAbsolutePath(out_list, separator);
515 // TODO: is there a reasonable equivalent to be written for win32?
518 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
520 resolved_path = link_path;
527 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
528 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
530 PathList_t::const_iterator si;
531 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
533 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
535 if ( one_shot && ! FoundPaths.empty() )
544 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
545 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
547 char name_buf[MaxFilePath];
550 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
552 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
554 if ( name_buf[0] == '.' ) continue; // no hidden files
555 std::string tmp_path = SearchDir + separator + name_buf;
557 if ( PathIsDirectory(tmp_path.c_str()) )
558 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
560 else if ( Pattern.Match(name_buf) )
562 FoundPaths.push_back(SearchDir + separator + name_buf);
576 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
578 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
583 regerror(result, &m_regex, buf, 128);
584 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
589 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
590 m_regex = rhs.m_regex;
593 Kumu::PathMatchRegex::~PathMatchRegex() {
598 Kumu::PathMatchRegex::Match(const std::string& s) const {
599 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
605 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
607 std::string regex; // convert glob to regex
609 for ( const char* p = glob.c_str(); *p != 0; p++ )
613 case '.': regex += "\\."; break;
614 case '*': regex += ".*"; break;
615 case '?': regex += ".?"; break;
616 default: regex += *p;
621 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
626 regerror(result, &m_regex, buf, 128);
627 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
632 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
633 m_regex = rhs.m_regex;
636 Kumu::PathMatchGlob::~PathMatchGlob() {
641 Kumu::PathMatchGlob::Match(const std::string& s) const {
642 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
648 //------------------------------------------------------------------------------------------
650 #define X_BUFSIZE 1024
654 Kumu::GetExecutablePath(const std::string& default_path)
656 char path[X_BUFSIZE] = {0};
657 bool success = false;
659 #if defined(KM_WIN32)
660 DWORD size = X_BUFSIZE;
661 DWORD rc = GetModuleFileName(0, path, size);
662 success = ( rc != 0 );
663 #elif defined(KM_MACOSX)
664 uint32_t size = X_BUFSIZE;
665 int rc = _NSGetExecutablePath(path, &size);
666 success = ( rc != -1 );
667 #elif defined(__linux__)
668 size_t size = X_BUFSIZE;
669 ssize_t rc = readlink("/proc/self/exe", path, size);
670 success = ( rc != -1 );
671 #elif defined(__OpenBSD__)
672 // This fails if the CWD changes after the program has started but before the
673 // call to GetExecutablePath(). For least surprise, call GetExecutablePath()
674 // immediately in main() and save the value for later use.
675 const char* p = getenv("_");
678 return Kumu::PathMakeAbsolute(p);
680 #elif defined(__FreeBSD__)
682 size_t size = X_BUFSIZE;
683 ssize_t rc = readlink("/proc/curproc/file", path, size);
684 success = ( rc != -1 );
685 #elif defined(__NetBSD__)
686 size_t size = X_BUFSIZE;
687 ssize_t rc = readlink("/proc/curproc/exe", path, size);
688 success = ( rc != -1 );
689 #elif defined(__sun) && defined(__SVR4)
690 size_t size = X_BUFSIZE;
691 char program[MAXPATHLEN];
692 snprintf(program, MAXPATHLEN, "/proc/%d/path/a.out", getpid());
693 ssize_t rc = readlink(program, path, size);
695 #error GetExecutablePath --> Create a method for obtaining the executable name
700 return Kumu::PathMakeCanonical(path);
707 //------------------------------------------------------------------------------------------
708 // portable aspects of the file classes
710 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
713 class Kumu::FileWriter::h__iovec
717 struct iovec m_iovec[IOVecMaxEntries];
718 h__iovec() : m_Count(0) {}
725 Kumu::FileReader::Size() const
729 GetFileSizeEx(m_Handle, &size);
730 return size.QuadPart;
734 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
736 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
737 return(info.st_size);
744 // these are declared here instead of in the header file
745 // because we have a mem_ptr that is managing a hidden class
746 Kumu::FileWriter::FileWriter()
749 Kumu::FileWriter::~FileWriter() {}
753 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
755 assert( ! m_IOVec.empty() );
756 register h__iovec* iov = m_IOVec;
759 if ( iov->m_Count >= IOVecMaxEntries )
761 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
763 return RESULT_WRITEFAIL;
766 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
767 iov->m_iovec[iov->m_Count].iov_len = buf_len;
774 Kumu::FileWriter::StartHashing()
777 MD5_Init(&m_MD5Context);
781 Kumu::FileWriter::MaybeHash(void const * data, int size)
785 MD5_Update (&m_MD5Context, data, size);
790 Kumu::FileWriter::StopHashing()
794 unsigned char digest[MD5_DIGEST_LENGTH];
795 MD5_Final (digest, &m_MD5Context);
801 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
802 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
824 Kumu::FileReader::FileReader()
826 m_Handle = INVALID_HANDLE_VALUE;
827 assert(sizeof(off_t) <= sizeof(int64_t));
830 Kumu::FileReader::~FileReader()
832 Kumu::FileReader::Close();
840 Kumu::wbstr_to_utf8(const Kumu::ByteString& in, std::string& out)
843 assert(in.Length()%sizeof(wchar_t)==0);
844 const wchar_t* p = (const wchar_t*)in.RoData();
846 int stringLength = static_cast<int>( in.Length() );
847 int len = WideCharToMultiByte(CP_UTF8, 0, p, stringLength, NULL, 0, NULL, NULL);
848 char *mb_buf = new char[len];
849 WideCharToMultiByte(CP_UTF8, 0, p, stringLength, mb_buf, len, NULL, NULL);
857 Kumu::utf8_to_wbstr(const std::string& in, Kumu::ByteString& out)
859 Result_t result = out.Capacity((in.size()+1)*sizeof(wchar_t));
861 if ( KM_FAILURE(result) )
866 assert(in.size()*sizeof(wchar_t)<=out.Capacity());
867 const char* read_pos = in.c_str();
868 wchar_t character, *write_pos = (wchar_t*)out.Data();
870 int stringLength = static_cast<int>( in.length() ) + 1;
871 int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, 0, 0);
872 result = out.Capacity(len*sizeof(wchar_t));
873 if ( KM_FAILURE(result) )
877 MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, write_pos, len);
878 out.Length(len*sizeof(wchar_t));
883 #endif // KM_WIN32_UTF8
885 //------------------------------------------------------------------------------------------
889 Kumu::FileReader::OpenRead(const std::string& filename) const
891 const_cast<FileReader*>(this)->m_Filename = filename;
893 ByteString wb_filename;
894 Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
896 if ( KM_FAILURE(result) )
902 // suppress popup window on error
903 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
906 const_cast<FileReader*>(this)->m_Handle =
907 ::CreateFileW((wchar_t*)wb_filename.RoData(),
909 const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename.c_str(),
911 (GENERIC_READ), // open for reading
912 FILE_SHARE_READ, // share for reading
914 OPEN_EXISTING, // read
915 FILE_ATTRIBUTE_NORMAL, // normal file
916 NULL // no template file
919 ::SetErrorMode(prev);
921 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
922 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
927 Kumu::FileReader::Close() const
929 if ( m_Handle == INVALID_HANDLE_VALUE )
930 return Kumu::RESULT_FILEOPEN;
932 // suppress popup window on error
933 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
934 BOOL result = ::CloseHandle(m_Handle);
935 ::SetErrorMode(prev);
936 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
938 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
943 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
945 if ( m_Handle == INVALID_HANDLE_VALUE )
946 return Kumu::RESULT_STATE;
949 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
950 in.QuadPart = position;
951 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
952 HRESULT LastError = GetLastError();
953 ::SetErrorMode(prev);
955 if ( (LastError != NO_ERROR
956 && (in.LowPart == INVALID_SET_FILE_POINTER
957 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
958 return Kumu::RESULT_READFAIL;
960 return Kumu::RESULT_OK;
965 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
969 if ( m_Handle == INVALID_HANDLE_VALUE )
970 return Kumu::RESULT_FILEOPEN;
973 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
974 in.QuadPart = (__int64)0;
975 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
976 HRESULT LastError = GetLastError();
977 ::SetErrorMode(prev);
979 if ( (LastError != NO_ERROR
980 && (in.LowPart == INVALID_SET_FILE_POINTER
981 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
982 return Kumu::RESULT_READFAIL;
984 *pos = (Kumu::fpos_t)in.QuadPart;
985 return Kumu::RESULT_OK;
990 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
993 Result_t result = Kumu::RESULT_OK;
997 if ( read_count == 0 )
998 read_count = &tmp_int;
1002 if ( m_Handle == INVALID_HANDLE_VALUE )
1003 return Kumu::RESULT_FILEOPEN;
1005 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1006 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
1007 result = Kumu::RESULT_READFAIL;
1009 ::SetErrorMode(prev);
1011 if ( tmp_count == 0 ) /* EOF */
1012 result = Kumu::RESULT_ENDOFFILE;
1014 if ( KM_SUCCESS(result) )
1015 *read_count = tmp_count;
1022 //------------------------------------------------------------------------------------------
1028 Kumu::FileWriter::OpenWrite(const std::string& filename)
1030 m_Filename = filename;
1031 #ifdef KM_WIN32_UTF8
1032 ByteString wb_filename;
1033 Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
1035 if ( KM_FAILURE(result) )
1041 // suppress popup window on error
1042 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1044 #ifdef KM_WIN32_UTF8
1045 m_Handle = ::CreateFileW((wchar_t*)wb_filename.RoData(),
1047 m_Handle = ::CreateFileA(filename.c_str(),
1049 (GENERIC_WRITE|GENERIC_READ), // open for reading
1050 FILE_SHARE_READ, // share for reading
1051 NULL, // no security
1052 CREATE_ALWAYS, // overwrite (beware!)
1053 FILE_ATTRIBUTE_NORMAL, // normal file
1054 NULL // no template file
1057 ::SetErrorMode(prev);
1059 if ( m_Handle == INVALID_HANDLE_VALUE )
1060 return Kumu::RESULT_FILEOPEN;
1062 m_IOVec = new h__iovec;
1063 return Kumu::RESULT_OK;
1067 /** @param filename File name (UTF-8 encoded) */
1069 Kumu::FileWriter::OpenModify(const std::string& filename)
1071 m_Filename = filename;
1073 // suppress popup window on error
1074 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1076 int const wn = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, 0, 0);
1077 wchar_t* buffer = new wchar_t[wn];
1078 if (MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0) {
1080 return Kumu::RESULT_FAIL;
1083 m_Handle = ::CreateFileW(buffer,
1084 (GENERIC_WRITE|GENERIC_READ), // open for reading
1085 FILE_SHARE_READ, // share for reading
1086 NULL, // no security
1087 OPEN_ALWAYS, // don't truncate existing
1088 FILE_ATTRIBUTE_NORMAL, // normal file
1089 NULL // no template file
1093 HRESULT const last_error = GetLastError();
1095 ::SetErrorMode(prev);
1097 if (m_Handle == INVALID_HANDLE_VALUE)
1099 DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
1100 return Kumu::RESULT_FILEOPEN;
1103 m_IOVec = new h__iovec;
1104 return Kumu::RESULT_OK;
1110 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1112 assert( ! m_IOVec.empty() );
1113 register h__iovec* iov = m_IOVec;
1116 if ( bytes_written == 0 )
1117 bytes_written = &tmp_int;
1119 if ( m_Handle == INVALID_HANDLE_VALUE )
1120 return Kumu::RESULT_STATE;
1123 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1124 Result_t result = Kumu::RESULT_OK;
1126 // AFAIK, there is no writev() equivalent in the win32 API
1127 for ( register int i = 0; i < iov->m_Count; i++ )
1129 ui32_t tmp_count = 0;
1130 BOOL wr_result = ::WriteFile(m_Handle,
1131 iov->m_iovec[i].iov_base,
1132 iov->m_iovec[i].iov_len,
1136 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1138 result = Kumu::RESULT_WRITEFAIL;
1142 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1143 *bytes_written += tmp_count;
1146 ::SetErrorMode(prev);
1147 iov->m_Count = 0; // error nor not, all is lost
1154 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1156 KM_TEST_NULL_L(buf);
1159 if ( bytes_written == 0 )
1160 bytes_written = &tmp_int;
1162 if ( m_Handle == INVALID_HANDLE_VALUE )
1163 return Kumu::RESULT_STATE;
1165 // suppress popup window on error
1166 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1167 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1168 ::SetErrorMode(prev);
1170 if ( result == 0 || *bytes_written != buf_len )
1171 return Kumu::RESULT_WRITEFAIL;
1173 MaybeHash(buf, buf_len);
1175 return Kumu::RESULT_OK;
1179 //------------------------------------------------------------------------------------------
1185 Kumu::FileReader::OpenRead(const std::string& filename) const
1187 const_cast<FileReader*>(this)->m_Filename = filename;
1188 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1189 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1194 Kumu::FileReader::Close() const
1196 if ( m_Handle == -1L )
1197 return RESULT_FILEOPEN;
1200 const_cast<FileReader*>(this)->m_Handle = -1L;
1206 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1208 if ( m_Handle == -1L )
1209 return RESULT_FILEOPEN;
1211 if ( lseek(m_Handle, position, whence) == -1L )
1212 return RESULT_BADSEEK;
1219 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1221 KM_TEST_NULL_L(pos);
1223 if ( m_Handle == -1L )
1224 return RESULT_FILEOPEN;
1226 Kumu::fpos_t tmp_pos;
1228 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1229 return RESULT_READFAIL;
1237 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1239 KM_TEST_NULL_L(buf);
1240 i32_t tmp_count = 0;
1243 if ( read_count == 0 )
1244 read_count = &tmp_int;
1248 if ( m_Handle == -1L )
1249 return RESULT_FILEOPEN;
1251 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1252 return RESULT_READFAIL;
1254 *read_count = tmp_count;
1255 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1258 //------------------------------------------------------------------------------------------
1263 Kumu::FileWriter::OpenWrite(const std::string& filename)
1265 m_Filename = filename;
1266 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0666);
1268 if ( m_Handle == -1L )
1270 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1271 return RESULT_FILEOPEN;
1274 m_IOVec = new h__iovec;
1280 Kumu::FileWriter::OpenModify(const std::string& filename)
1282 m_Filename = filename;
1283 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0666);
1285 if ( m_Handle == -1L )
1287 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1288 return RESULT_FILEOPEN;
1291 m_IOVec = new h__iovec;
1297 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1299 assert( ! m_IOVec.empty() );
1300 register h__iovec* iov = m_IOVec;
1303 if ( bytes_written == 0 )
1304 bytes_written = &tmp_int;
1306 if ( m_Handle == -1L )
1307 return RESULT_STATE;
1310 for ( int i = 0; i < iov->m_Count; i++ )
1311 total_size += iov->m_iovec[i].iov_len;
1313 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1315 if ( write_size == -1L || write_size != total_size )
1316 return RESULT_WRITEFAIL;
1318 for (int i = 0; i < iov->m_Count; ++i)
1320 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1324 *bytes_written = write_size;
1330 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1332 KM_TEST_NULL_L(buf);
1335 if ( bytes_written == 0 )
1336 bytes_written = &tmp_int;
1338 if ( m_Handle == -1L )
1339 return RESULT_STATE;
1341 int write_size = write(m_Handle, buf, buf_len);
1343 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1344 return RESULT_WRITEFAIL;
1346 MaybeHash(buf, buf_len);
1347 *bytes_written = write_size;
1354 //------------------------------------------------------------------------------------------
1357 IFileReader* FileReaderFactory::CreateFileReader() const
1359 return new FileReader();
1364 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1367 ui32_t read_size = 0;
1371 Result_t result = File.OpenRead(filename);
1373 if ( KM_SUCCESS(result) )
1375 fsize = File.Size();
1377 if ( fsize > (Kumu::fpos_t)max_size )
1379 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1380 return RESULT_ALLOC;
1389 result = ReadBuf.Capacity((ui32_t)fsize);
1392 if ( KM_SUCCESS(result) )
1393 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1395 if ( KM_SUCCESS(result) )
1396 outString.assign((const char*)ReadBuf.RoData(), read_size);
1404 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1407 ui32_t write_count = 0;
1409 Result_t result = File.OpenWrite(filename);
1411 if ( KM_SUCCESS(result) )
1412 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1417 //------------------------------------------------------------------------------------------
1422 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1425 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1426 Result_t result = Buffer.Capacity(file_size);
1428 if ( KM_SUCCESS(result) )
1430 ui32_t read_count = 0;
1433 result = Reader.OpenRead(Filename);
1435 if ( KM_SUCCESS(result) )
1436 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1438 if ( KM_SUCCESS(result) )
1440 assert(file_size == read_count);
1441 Buffer.Length(read_count);
1442 MemIOReader MemReader(&Buffer);
1443 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1452 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1455 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1457 if ( KM_SUCCESS(result) )
1459 ui32_t write_count = 0;
1461 MemIOWriter MemWriter(&Buffer);
1463 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1465 if ( KM_SUCCESS(result) )
1467 Buffer.Length(MemWriter.Length());
1468 result = Writer.OpenWrite(Filename);
1471 if ( KM_SUCCESS(result) )
1472 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1478 //------------------------------------------------------------------------------------------
1483 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1485 ui32_t file_size = FileSize(Filename);
1486 Result_t result = Buffer.Capacity(file_size);
1488 if ( KM_SUCCESS(result) )
1490 ui32_t read_count = 0;
1493 result = Reader.OpenRead(Filename);
1495 if ( KM_SUCCESS(result) )
1496 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1498 if ( KM_SUCCESS(result) )
1500 if ( file_size != read_count)
1501 return RESULT_READFAIL;
1503 Buffer.Length(read_count);
1512 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1514 ui32_t write_count = 0;
1517 Result_t result = Writer.OpenWrite(Filename);
1519 if ( KM_SUCCESS(result) )
1520 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1522 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1523 return RESULT_WRITEFAIL;
1528 //------------------------------------------------------------------------------------------
1533 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1537 Kumu::DirScanner::Open(const std::string& dirname)
1539 Result_t result = RESULT_OK;
1541 if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1547 result = RESULT_NOTAFILE;
1550 result = RESULT_NO_PERM;
1554 result = RESULT_PARAM;
1558 result = RESULT_STATE;
1561 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1562 result = RESULT_FAIL;
1573 Kumu::DirScanner::Close()
1575 if ( m_Handle == NULL )
1576 return RESULT_FILEOPEN;
1578 if ( closedir(m_Handle) == -1 ) {
1583 return RESULT_STATE;
1585 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1597 Kumu::DirScanner::GetNext(char* filename)
1599 KM_TEST_NULL_L(filename);
1601 if ( m_Handle == NULL )
1602 return RESULT_FILEOPEN;
1604 struct dirent* entry;
1608 if ( ( entry = readdir(m_Handle)) == NULL )
1609 return RESULT_ENDOFFILE;
1614 strncpy(filename, entry->d_name, MaxFilePath);
1618 //------------------------------------------------------------------------------------------
1621 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1625 Kumu::DirScannerEx::Open(const std::string& dirname)
1627 Result_t result = RESULT_OK;
1629 if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1635 result = RESULT_NOTAFILE;
1638 result = RESULT_NO_PERM;
1642 result = RESULT_PARAM;
1646 result = RESULT_STATE;
1649 DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1650 result = RESULT_FAIL;
1655 if ( KM_SUCCESS(result) )
1656 m_Dirname = dirname;
1658 KM_RESULT_STATE_TEST_IMPLICIT();
1664 Kumu::DirScannerEx::Close()
1666 if ( m_Handle == NULL )
1667 return RESULT_FILEOPEN;
1669 if ( closedir(m_Handle) == -1 )
1675 KM_RESULT_STATE_HERE();
1676 return RESULT_STATE;
1679 DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1690 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1692 if ( m_Handle == 0 )
1693 return RESULT_FILEOPEN;
1695 #if defined(__sun) && defined(__SVR4)
1698 struct dirent* entry;
1702 if ( ( entry = readdir(m_Handle) ) == 0 )
1703 return RESULT_ENDOFFILE;
1708 next_item_name.assign(entry->d_name, strlen(entry->d_name));
1710 #if defined(__sun) && defined(__SVR4)
1712 stat(entry->d_name, &s);
1714 switch ( s.st_mode )
1717 next_item_type = DET_DIR;
1721 next_item_type = DET_FILE;
1725 next_item_type = DET_LINK;
1729 next_item_type = DET_DEV;
1732 switch ( entry->d_type )
1735 next_item_type = DET_DIR;
1739 next_item_type = DET_FILE;
1743 next_item_type = DET_LINK;
1747 next_item_type = DET_DEV;
1754 //------------------------------------------------------------------------------------------
1757 // Attention Windows users: make sure to use the proper separator character
1758 // with these functions.
1761 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1764 Kumu::CreateDirectoriesInPath(const std::string& Path)
1766 bool abs = PathIsAbsolute(Path);
1767 PathCompList_t PathComps, TmpPathComps;
1769 PathToComponents(Path, PathComps);
1771 while ( ! PathComps.empty() )
1773 TmpPathComps.push_back(PathComps.front());
1774 PathComps.pop_front();
1775 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1777 if ( ! PathIsDirectory(tmp_path) )
1780 if ( _mkdir(tmp_path.c_str()) != 0 )
1782 if ( mkdir(tmp_path.c_str(), 0777) != 0 )
1785 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1786 tmp_path.c_str(), strerror(errno));
1787 return RESULT_DIR_CREATE;
1798 Kumu::DeleteFile(const std::string& filename)
1800 if ( _unlink(filename.c_str()) == 0 )
1806 case ENOTDIR: return RESULT_NOTAFILE;
1811 case EPERM: return RESULT_NO_PERM;
1814 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1823 h__DeletePath(const std::string& pathname)
1825 if ( pathname.empty() )
1826 return RESULT_NULL_STR;
1828 Result_t result = RESULT_OK;
1830 if ( ! PathIsDirectory(pathname) )
1832 result = DeleteFile(pathname);
1838 char next_file[Kumu::MaxFilePath];
1839 result = TestDir.Open(pathname.c_str());
1841 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1843 if ( next_file[0] == '.' )
1845 if ( next_file[1] == 0 )
1846 continue; // don't delete 'this'
1848 if ( next_file[1] == '.' && next_file[2] == 0 )
1849 continue; // don't delete 'this' parent
1852 result = h__DeletePath(pathname + std::string("/") + next_file);
1856 if ( _rmdir(pathname.c_str()) != 0 )
1862 result = RESULT_NOTAFILE;
1869 result = RESULT_NO_PERM;
1873 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1874 result = RESULT_FAIL;
1886 Kumu::DeletePath(const std::string& pathname)
1888 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1889 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1890 return h__DeletePath(c_pathname);
1896 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1898 DirScanner source_dir;
1899 char next_file[Kumu::MaxFilePath];
1901 Result_t result = source_dir.Open(path);
1903 if ( KM_FAILURE(result) )
1906 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1908 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1909 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1912 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1915 return DeletePath(path);
1919 //------------------------------------------------------------------------------------------
1924 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1927 ULARGE_INTEGER lTotalNumberOfBytes;
1928 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1930 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1933 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1934 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1938 HRESULT last_error = ::GetLastError();
1940 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1945 #if defined(__sun) && defined(__SVR4)
1946 if ( statfs(path.c_str(), &s, s.f_bsize, s.f_fstyp ) == 0 )
1947 { if ( s.f_blocks < 1 )
1949 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1950 path.c_str(), s.f_blocks);
1954 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bfree;
1955 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1959 if ( statfs(path.c_str(), &s) == 0 )
1961 if ( s.f_blocks < 1 )
1963 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1964 path.c_str(), s.f_blocks);
1968 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1969 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1976 case ENOTDIR: return RESULT_NOTAFILE;
1977 case EACCES: return RESULT_NO_PERM;
1980 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1988 // end KM_fileio.cpp