2 Copyright (c) 2004-2014, 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
28 \version $Id: KM_fileio.cpp,v 1.40 2015/10/07 16:58:03 jhurst Exp $
29 \brief portable file i/o
32 #include <asdcp/KM_fileio.h>
33 #include <asdcp/KM_log.h>
42 #define _getcwd getcwd
43 #define _unlink unlink
47 // only needed by GetExecutablePath()
48 #if defined(KM_MACOSX)
49 #include <mach-o/dyld.h>
55 typedef struct _stati64 fstat_t;
59 // win32 has WriteFileGather() and ReadFileScatter() but they
60 // demand page alignment and page sizing, making them unsuitable
61 // for use with arbitrary buffer sizes.
63 char* iov_base; // stupid iovec uses char*
67 # if defined(__linux__)
68 # include <sys/statfs.h>
70 # include <sys/param.h>
71 # include <sys/mount.h>
76 typedef struct stat fstat_t;
82 do_stat(const char* path, fstat_t* stat_info)
84 KM_TEST_NULL_STR_L(path);
85 KM_TEST_NULL_L(stat_info);
87 Kumu::Result_t result = Kumu::RESULT_OK;
90 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
92 int const wn = MultiByteToWideChar (CP_UTF8, 0, path, -1, 0, 0);
93 wchar_t* buffer = new wchar_t[wn];
94 if (MultiByteToWideChar (CP_UTF8, 0, path, -1, buffer, wn) == 0)
97 return Kumu::RESULT_FAIL;
100 if ( _wstati64(buffer, stat_info) == (__int64)-1 )
101 result = Kumu::RESULT_FILEOPEN;
105 ::SetErrorMode( prev );
107 if ( stat(path, stat_info) == -1L )
108 result = Kumu::RESULT_FILEOPEN;
110 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
111 result = Kumu::RESULT_FILEOPEN;
120 static Kumu::Result_t
121 do_fstat(FileHandle handle, fstat_t* stat_info)
123 KM_TEST_NULL_L(stat_info);
125 Kumu::Result_t result = Kumu::RESULT_OK;
127 if ( fstat(handle, stat_info) == -1L )
128 result = Kumu::RESULT_FILEOPEN;
130 if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
131 result = Kumu::RESULT_FILEOPEN;
141 Kumu::PathExists(const std::string& pathname)
143 if ( pathname.empty() )
148 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
156 Kumu::PathIsFile(const std::string& pathname)
158 if ( pathname.empty() )
163 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
165 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
175 Kumu::PathIsDirectory(const std::string& pathname)
177 if ( pathname.empty() )
182 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
184 if ( info.st_mode & S_IFDIR )
193 Kumu::FileSize(const std::string& pathname)
195 if ( pathname.empty() )
200 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
202 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
203 return(info.st_size);
211 make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
213 PathCompList_t::const_iterator i;
214 for ( i = in_list.begin(); i != in_list.end(); ++i )
218 if ( ! out_list.empty() )
223 else if ( *i != "." )
225 out_list.push_back(*i);
232 Kumu::PathMakeCanonical(const std::string& Path, char separator)
234 PathCompList_t in_list, out_list;
235 bool is_absolute = PathIsAbsolute(Path, separator);
236 PathToComponents(Path, in_list, separator);
237 make_canonical_list(in_list, out_list);
240 return ComponentsToAbsolutePath(out_list, separator);
242 return ComponentsToPath(out_list, separator);
247 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
249 return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
253 Kumu::PathCompList_t&
254 Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
258 PathCompList_t tmp_list = km_token_split(path, std::string(s));
259 PathCompList_t::const_iterator i;
261 for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
265 component_list.push_back(*i);
269 return component_list;
274 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
279 PathCompList_t::const_iterator ci = CList.begin();
280 std::string out_path = *ci;
282 for ( ci++; ci != CList.end(); ci++ )
283 out_path += separator + *ci;
290 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
292 std::string out_path;
295 out_path = separator;
298 PathCompList_t::const_iterator ci;
300 for ( ci = CList.begin(); ci != CList.end(); ci++ )
301 out_path += separator + *ci;
309 Kumu::PathHasComponents(const std::string& Path, char separator)
311 if ( strchr(Path.c_str(), separator) == 0 )
319 Kumu::PathIsAbsolute(const std::string& Path, char separator)
324 if ( Path[0] == separator)
334 char cwd_buf [MaxFilePath];
335 if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
337 DefaultLogSink().Error("Error retrieving current working directory.");
346 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
355 if ( PathIsAbsolute(Path, separator) )
356 return PathMakeCanonical(Path);
358 PathCompList_t in_list, out_list;
359 PathToComponents(PathJoin(PathCwd(), Path), in_list);
360 make_canonical_list(in_list, out_list);
362 return ComponentsToAbsolutePath(out_list);
367 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
369 size_t pos = Path.find(Parent);
371 if ( pos == 0 ) // Parent found at offset 0
372 return Path.substr(Parent.size()+1);
379 Kumu::PathBasename(const std::string& Path, char separator)
381 PathCompList_t CList;
382 PathToComponents(Path, CList, separator);
392 Kumu::PathDirname(const std::string& Path, char separator)
394 PathCompList_t CList;
395 bool is_absolute = PathIsAbsolute(Path, separator);
396 PathToComponents(Path, CList, separator);
399 return is_absolute ? "/" : "";
404 return ComponentsToAbsolutePath(CList, separator);
406 return ComponentsToPath(CList, separator);
411 Kumu::PathGetExtension(const std::string& Path)
413 std::string Basename = PathBasename(Path);
414 const char* p = strrchr(Basename.c_str(), '.');
424 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
426 std::string Basename = PathBasename(Path);
427 const char* p = strrchr(Basename.c_str(), '.');
430 Basename = Basename.substr(0, p - Basename.c_str());
432 if ( Extension.empty() )
435 return Basename + "." + Extension;
440 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
442 return Path1 + separator + Path2;
447 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
449 return Path1 + separator + Path2 + separator + Path3;
454 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
455 const std::string& Path3, const std::string& Path4, char separator)
457 return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
461 // returns false if link cannot be read
464 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
466 PathCompList_t in_list, out_list;
467 PathToComponents(PathMakeCanonical(link_path), in_list, separator);
468 PathCompList_t::iterator i;
469 char link_buf[MaxFilePath];
471 for ( i = in_list.begin(); i != in_list.end(); ++i )
473 assert ( *i != ".." && *i != "." );
474 out_list.push_back(*i);
478 std::string next_link = ComponentsToAbsolutePath(out_list, separator);
479 ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
481 if ( link_size == -1 )
483 if ( errno == EINVAL )
486 DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
490 assert(link_size < MaxFilePath);
491 link_buf[link_size] = 0;
492 std::string tmp_path;
495 if ( PathIsAbsolute(link_buf) )
501 tmp_path = PathJoin(PathDirname(next_link), link_buf);
504 PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
508 resolved_path = ComponentsToAbsolutePath(out_list, separator);
513 // TODO: is there a reasonable equivalent to be written for win32?
516 Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
518 resolved_path = link_path;
525 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
526 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
528 PathList_t::const_iterator si;
529 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
531 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
533 if ( one_shot && ! FoundPaths.empty() )
542 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
543 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
545 char name_buf[MaxFilePath];
548 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
550 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
552 if ( name_buf[0] == '.' ) continue; // no hidden files
553 std::string tmp_path = SearchDir + separator + name_buf;
555 if ( PathIsDirectory(tmp_path.c_str()) )
556 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
558 else if ( Pattern.Match(name_buf) )
560 FoundPaths.push_back(SearchDir + separator + name_buf);
574 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
576 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
581 regerror(result, &m_regex, buf, 128);
582 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
587 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
588 m_regex = rhs.m_regex;
591 Kumu::PathMatchRegex::~PathMatchRegex() {
596 Kumu::PathMatchRegex::Match(const std::string& s) const {
597 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
603 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
605 std::string regex; // convert glob to regex
607 for ( const char* p = glob.c_str(); *p != 0; p++ )
611 case '.': regex += "\\."; break;
612 case '*': regex += ".*"; break;
613 case '?': regex += ".?"; break;
614 default: regex += *p;
619 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
624 regerror(result, &m_regex, buf, 128);
625 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
630 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
631 m_regex = rhs.m_regex;
634 Kumu::PathMatchGlob::~PathMatchGlob() {
639 Kumu::PathMatchGlob::Match(const std::string& s) const {
640 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
646 //------------------------------------------------------------------------------------------
648 #define X_BUFSIZE 1024
652 Kumu::GetExecutablePath(const std::string& default_path)
654 char path[X_BUFSIZE] = {0};
655 bool success = false;
657 #if defined(KM_WIN32)
658 DWORD size = X_BUFSIZE;
659 DWORD rc = GetModuleFileName(0, path, size);
660 success = ( rc != 0 );
661 #elif defined(KM_MACOSX)
662 uint32_t size = X_BUFSIZE;
663 int rc = _NSGetExecutablePath(path, &size);
664 success = ( rc != -1 );
665 #elif defined(__linux__)
666 size_t size = X_BUFSIZE;
667 ssize_t rc = readlink("/proc/self/exe", path, size);
668 success = ( rc != -1 );
669 #elif defined(__OpenBSD__) || defined(__FreeBSD__)
670 size_t size = X_BUFSIZE;
671 ssize_t rc = readlink("/proc/curproc/file", path, size);
672 success = ( rc != -1 );
673 #elif defined(__FreeBSD__)
674 size_t size = X_BUFSIZE;
675 ssize_t rc = readlink("/proc/curproc/file", path, size);
676 success = ( rc != -1 );
677 #elif defined(__NetBSD__)
678 size_t size = X_BUFSIZE;
679 ssize_t rc = readlink("/proc/curproc/file", path, size);
680 success = ( rc != -1 );
682 #error GetExecutablePath --> Create a method for obtaining the executable name
687 return Kumu::PathMakeCanonical(path);
694 //------------------------------------------------------------------------------------------
695 // portable aspects of the file classes
697 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
700 class Kumu::FileWriter::h__iovec
704 struct iovec m_iovec[IOVecMaxEntries];
705 h__iovec() : m_Count(0) {}
712 Kumu::FileReader::Size() const
715 return FileSize(m_Filename.c_str());
719 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
721 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
722 return(info.st_size);
729 // these are declared here instead of in the header file
730 // because we have a mem_ptr that is managing a hidden class
731 Kumu::FileWriter::FileWriter()
734 Kumu::FileWriter::~FileWriter() {}
738 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
740 assert( ! m_IOVec.empty() );
741 register h__iovec* iov = m_IOVec;
744 if ( iov->m_Count >= IOVecMaxEntries )
746 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
748 return RESULT_WRITEFAIL;
751 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
752 iov->m_iovec[iov->m_Count].iov_len = buf_len;
759 Kumu::FileWriter::StartHashing()
762 MD5_Init (&m_MD5Context);
766 Kumu::FileWriter::MaybeHash(void const * data, int size)
770 MD5_Update (&m_MD5Context, data, size);
775 Kumu::FileWriter::StopHashing()
779 unsigned char digest[MD5_DIGEST_LENGTH];
780 MD5_Final (digest, &m_MD5Context);
782 char hex[MD5_DIGEST_LENGTH * 2 + 1];
783 for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
785 sprintf(hex + i * 2, "%02x", digest[i]);
795 //------------------------------------------------------------------------------------------
799 Kumu::FileReader::OpenRead(const std::string& filename) const
801 const_cast<FileReader*>(this)->m_Filename = filename;
803 // suppress popup window on error
804 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
806 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
807 wchar_t* buffer = new wchar_t[wn];
808 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0)
811 return Kumu::RESULT_FAIL;
813 const_cast<FileReader*>(this)->m_Handle = ::CreateFileW(buffer,
814 (GENERIC_READ), // open for reading
815 FILE_SHARE_READ, // share for reading
817 OPEN_EXISTING, // read
818 FILE_ATTRIBUTE_NORMAL, // normal file
819 NULL // no template file
824 ::SetErrorMode(prev);
826 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
827 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
832 Kumu::FileReader::Close() const
834 if ( m_Handle == INVALID_HANDLE_VALUE )
835 return Kumu::RESULT_FILEOPEN;
837 // suppress popup window on error
838 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
839 BOOL result = ::CloseHandle(m_Handle);
840 ::SetErrorMode(prev);
841 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
843 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
848 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
850 if ( m_Handle == INVALID_HANDLE_VALUE )
851 return Kumu::RESULT_STATE;
854 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
855 in.QuadPart = position;
856 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
857 HRESULT LastError = GetLastError();
858 ::SetErrorMode(prev);
860 if ( (LastError != NO_ERROR
861 && (in.LowPart == INVALID_SET_FILE_POINTER
862 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
863 return Kumu::RESULT_READFAIL;
865 return Kumu::RESULT_OK;
870 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
874 if ( m_Handle == INVALID_HANDLE_VALUE )
875 return Kumu::RESULT_FILEOPEN;
878 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
879 in.QuadPart = (__int64)0;
880 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
881 HRESULT LastError = GetLastError();
882 ::SetErrorMode(prev);
884 if ( (LastError != NO_ERROR
885 && (in.LowPart == INVALID_SET_FILE_POINTER
886 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
887 return Kumu::RESULT_READFAIL;
889 *pos = (Kumu::fpos_t)in.QuadPart;
890 return Kumu::RESULT_OK;
895 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
898 Result_t result = Kumu::RESULT_OK;
902 if ( read_count == 0 )
903 read_count = &tmp_int;
907 if ( m_Handle == INVALID_HANDLE_VALUE )
908 return Kumu::RESULT_FILEOPEN;
910 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
911 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
912 result = Kumu::RESULT_READFAIL;
914 ::SetErrorMode(prev);
916 if ( tmp_count == 0 ) /* EOF */
917 result = Kumu::RESULT_ENDOFFILE;
919 if ( KM_SUCCESS(result) )
920 *read_count = tmp_count;
927 //------------------------------------------------------------------------------------------
932 Kumu::FileWriter::OpenWrite(const std::string& filename)
934 m_Filename = filename;
936 // suppress popup window on error
937 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
939 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
940 wchar_t* buffer = new wchar_t[wn];
941 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0)
944 return Kumu::RESULT_FAIL;
947 m_Handle = ::CreateFileW(buffer,
948 (GENERIC_WRITE|GENERIC_READ), // open for reading
949 FILE_SHARE_READ, // share for reading
951 CREATE_ALWAYS, // overwrite (beware!)
952 FILE_ATTRIBUTE_NORMAL, // normal file
953 NULL // no template file
958 ::SetErrorMode(prev);
960 if ( m_Handle == INVALID_HANDLE_VALUE )
961 return Kumu::RESULT_FILEOPEN;
963 m_IOVec = new h__iovec;
964 return Kumu::RESULT_OK;
967 /** @param filename File name (UTF-8 encoded) */
969 Kumu::FileWriter::OpenModify(const std::string& filename)
971 m_Filename = filename;
973 // suppress popup window on error
974 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
976 int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
977 wchar_t* buffer = new wchar_t[wn];
978 if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0) {
980 return Kumu::RESULT_FAIL;
983 m_Handle = ::CreateFileW(buffer,
984 (GENERIC_WRITE|GENERIC_READ), // open for reading
985 FILE_SHARE_READ, // share for reading
987 OPEN_ALWAYS, // don't truncate existing
988 FILE_ATTRIBUTE_NORMAL, // normal file
989 NULL // no template file
994 ::SetErrorMode(prev);
996 if ( m_Handle == INVALID_HANDLE_VALUE )
997 return Kumu::RESULT_FILEOPEN;
999 m_IOVec = new h__iovec;
1000 return Kumu::RESULT_OK;
1006 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1008 assert( ! m_IOVec.empty() );
1009 register h__iovec* iov = m_IOVec;
1012 if ( bytes_written == 0 )
1013 bytes_written = &tmp_int;
1015 if ( m_Handle == INVALID_HANDLE_VALUE )
1016 return Kumu::RESULT_STATE;
1019 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1020 Result_t result = Kumu::RESULT_OK;
1022 // AFAIK, there is no writev() equivalent in the win32 API
1023 for ( register int i = 0; i < iov->m_Count; i++ )
1025 ui32_t tmp_count = 0;
1026 BOOL wr_result = ::WriteFile(m_Handle,
1027 iov->m_iovec[i].iov_base,
1028 iov->m_iovec[i].iov_len,
1032 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1034 result = Kumu::RESULT_WRITEFAIL;
1038 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1039 *bytes_written += tmp_count;
1042 ::SetErrorMode(prev);
1043 iov->m_Count = 0; // error nor not, all is lost
1050 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1052 KM_TEST_NULL_L(buf);
1055 if ( bytes_written == 0 )
1056 bytes_written = &tmp_int;
1058 if ( m_Handle == INVALID_HANDLE_VALUE )
1059 return Kumu::RESULT_STATE;
1061 // suppress popup window on error
1062 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1063 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1064 ::SetErrorMode(prev);
1066 if ( result == 0 || *bytes_written != buf_len )
1067 return Kumu::RESULT_WRITEFAIL;
1069 MaybeHash(buf, buf_len);
1071 return Kumu::RESULT_OK;
1075 //------------------------------------------------------------------------------------------
1080 Kumu::FileReader::OpenRead(const std::string& filename) const
1082 const_cast<FileReader*>(this)->m_Filename = filename;
1083 const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1084 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1089 Kumu::FileReader::Close() const
1091 if ( m_Handle == -1L )
1092 return RESULT_FILEOPEN;
1095 const_cast<FileReader*>(this)->m_Handle = -1L;
1101 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1103 if ( m_Handle == -1L )
1104 return RESULT_FILEOPEN;
1106 if ( lseek(m_Handle, position, whence) == -1L )
1107 return RESULT_BADSEEK;
1114 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1116 KM_TEST_NULL_L(pos);
1118 if ( m_Handle == -1L )
1119 return RESULT_FILEOPEN;
1121 Kumu::fpos_t tmp_pos;
1123 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1124 return RESULT_READFAIL;
1132 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1134 KM_TEST_NULL_L(buf);
1135 i32_t tmp_count = 0;
1138 if ( read_count == 0 )
1139 read_count = &tmp_int;
1143 if ( m_Handle == -1L )
1144 return RESULT_FILEOPEN;
1146 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1147 return RESULT_READFAIL;
1149 *read_count = tmp_count;
1150 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1154 //------------------------------------------------------------------------------------------
1159 Kumu::FileWriter::OpenWrite(const std::string& filename)
1161 m_Filename = filename;
1162 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
1164 if ( m_Handle == -1L )
1166 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1167 return RESULT_FILEOPEN;
1170 m_IOVec = new h__iovec;
1176 Kumu::FileWriter::OpenModify(const std::string& filename)
1178 m_Filename = filename;
1179 m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1181 if ( m_Handle == -1L )
1183 DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1184 return RESULT_FILEOPEN;
1187 m_IOVec = new h__iovec;
1193 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1195 assert( ! m_IOVec.empty() );
1196 register h__iovec* iov = m_IOVec;
1199 if ( bytes_written == 0 )
1200 bytes_written = &tmp_int;
1202 if ( m_Handle == -1L )
1203 return RESULT_STATE;
1206 for ( int i = 0; i < iov->m_Count; i++ )
1207 total_size += iov->m_iovec[i].iov_len;
1209 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1211 if ( write_size == -1L || write_size != total_size )
1212 return RESULT_WRITEFAIL;
1214 for (int i = 0; i < iov->m_Count; ++i)
1216 MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1220 *bytes_written = write_size;
1226 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1228 KM_TEST_NULL_L(buf);
1231 if ( bytes_written == 0 )
1232 bytes_written = &tmp_int;
1234 if ( m_Handle == -1L )
1235 return RESULT_STATE;
1237 int write_size = write(m_Handle, buf, buf_len);
1238 MaybeHash(buf, buf_len);
1240 if ( write_size == -1L || (ui32_t)write_size != buf_len )
1241 return RESULT_WRITEFAIL;
1243 *bytes_written = write_size;
1250 //------------------------------------------------------------------------------------------
1255 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1258 ui32_t read_size = 0;
1262 Result_t result = File.OpenRead(filename);
1264 if ( KM_SUCCESS(result) )
1266 fsize = File.Size();
1268 if ( fsize > (Kumu::fpos_t)max_size )
1270 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1271 return RESULT_ALLOC;
1276 DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1277 return RESULT_READFAIL;
1280 result = ReadBuf.Capacity((ui32_t)fsize);
1283 if ( KM_SUCCESS(result) )
1284 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1286 if ( KM_SUCCESS(result) )
1287 outString.assign((const char*)ReadBuf.RoData(), read_size);
1295 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1298 ui32_t write_count = 0;
1300 Result_t result = File.OpenWrite(filename);
1302 if ( KM_SUCCESS(result) )
1303 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1308 //------------------------------------------------------------------------------------------
1313 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1316 ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1317 Result_t result = Buffer.Capacity(file_size);
1319 if ( KM_SUCCESS(result) )
1321 ui32_t read_count = 0;
1324 result = Reader.OpenRead(Filename);
1326 if ( KM_SUCCESS(result) )
1327 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1329 if ( KM_SUCCESS(result) )
1331 assert(file_size == read_count);
1332 Buffer.Length(read_count);
1333 MemIOReader MemReader(&Buffer);
1334 result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1343 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1346 Result_t result = Buffer.Capacity(Object.ArchiveLength());
1348 if ( KM_SUCCESS(result) )
1350 ui32_t write_count = 0;
1352 MemIOWriter MemWriter(&Buffer);
1354 result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1356 if ( KM_SUCCESS(result) )
1358 Buffer.Length(MemWriter.Length());
1359 result = Writer.OpenWrite(Filename);
1362 if ( KM_SUCCESS(result) )
1363 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1369 //------------------------------------------------------------------------------------------
1374 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1376 ui32_t file_size = FileSize(Filename);
1377 Result_t result = Buffer.Capacity(file_size);
1379 if ( KM_SUCCESS(result) )
1381 ui32_t read_count = 0;
1384 result = Reader.OpenRead(Filename);
1386 if ( KM_SUCCESS(result) )
1387 result = Reader.Read(Buffer.Data(), file_size, &read_count);
1389 if ( KM_SUCCESS(result) )
1391 if ( file_size != read_count)
1392 return RESULT_READFAIL;
1394 Buffer.Length(read_count);
1403 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1405 ui32_t write_count = 0;
1408 Result_t result = Writer.OpenWrite(Filename);
1410 if ( KM_SUCCESS(result) )
1411 result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1413 if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1414 return RESULT_WRITEFAIL;
1419 //------------------------------------------------------------------------------------------
1422 Kumu::DirScanner::DirScanner()
1423 : _iterator(boost::filesystem::directory_iterator())
1429 Kumu::DirScanner::Open(const std::string& filename)
1431 if (!boost::filesystem::is_directory(filename))
1432 return RESULT_NOT_FOUND;
1434 _iterator = boost::filesystem::directory_iterator(filename);
1441 Kumu::DirScanner::GetNext(char* filename)
1443 if (_iterator == boost::filesystem::directory_iterator())
1444 return RESULT_ENDOFFILE;
1446 #if BOOST_FILESYSTEM_VERSION == 3
1447 std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1449 std::string f = boost::filesystem::path(*_iterator).filename();
1451 strncpy(filename, f.c_str(), MaxFilePath);
1456 //------------------------------------------------------------------------------------------
1459 // Attention Windows users: make sure to use the proper separator character
1460 // with these functions.
1463 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1466 Kumu::CreateDirectoriesInPath(const std::string& Path)
1468 bool abs = PathIsAbsolute(Path);
1469 PathCompList_t PathComps, TmpPathComps;
1471 PathToComponents(Path, PathComps);
1473 while ( ! PathComps.empty() )
1475 TmpPathComps.push_back(PathComps.front());
1476 PathComps.pop_front();
1477 std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1479 if ( ! PathIsDirectory(tmp_path) )
1482 if ( _mkdir(tmp_path.c_str()) != 0 )
1484 if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1487 DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1488 tmp_path.c_str(), strerror(errno));
1489 return RESULT_DIR_CREATE;
1500 Kumu::DeleteFile(const std::string& filename)
1502 if ( _unlink(filename.c_str()) == 0 )
1508 case ENOTDIR: return RESULT_NOTAFILE;
1513 case EPERM: return RESULT_NO_PERM;
1516 DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1522 h__DeletePath(const std::string& pathname)
1524 if ( pathname.empty() )
1525 return RESULT_NULL_STR;
1527 Result_t result = RESULT_OK;
1529 if ( ! PathIsDirectory(pathname) )
1531 result = DeleteFile(pathname);
1537 char next_file[Kumu::MaxFilePath];
1538 result = TestDir.Open(pathname.c_str());
1540 while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1542 if ( next_file[0] == '.' )
1544 if ( next_file[1] == 0 )
1545 continue; // don't delete 'this'
1547 if ( next_file[1] == '.' && next_file[2] == 0 )
1548 continue; // don't delete 'this' parent
1551 result = h__DeletePath(pathname + std::string("/") + next_file);
1555 if ( _rmdir(pathname.c_str()) != 0 )
1561 result = RESULT_NOTAFILE;
1568 result = RESULT_NO_PERM;
1572 DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1573 result = RESULT_FAIL;
1583 Kumu::DeletePath(const std::string& pathname)
1585 std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1586 DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1587 return h__DeletePath(c_pathname);
1593 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1595 DirScanner source_dir;
1596 char next_file[Kumu::MaxFilePath];
1598 Result_t result = source_dir.Open(path);
1600 if ( KM_FAILURE(result) )
1603 while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1605 if ( ( next_file[0] == '.' && next_file[1] == 0 )
1606 || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1609 return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1612 return DeletePath(path);
1616 //------------------------------------------------------------------------------------------
1621 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1624 ULARGE_INTEGER lTotalNumberOfBytes;
1625 ULARGE_INTEGER lTotalNumberOfFreeBytes;
1627 BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1630 free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1631 total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1635 HRESULT last_error = ::GetLastError();
1637 DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1642 if ( statfs(path.c_str(), &s) == 0 )
1644 if ( s.f_blocks < 1 )
1646 DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1647 path.c_str(), s.f_blocks);
1651 free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1652 total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1659 case ENOTDIR: return RESULT_NOTAFILE;
1660 case EACCES: return RESULT_NO_PERM;
1663 DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1670 // end KM_fileio.cpp