X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2FKM_fileio.cpp;h=b9257abda88d7d9c87bd929cbce7a9bd806c47c7;hb=f6761c9d335a482925d7ad9b1b72543684afcb77;hp=4453e89d59ad48b6ec09c3046370d7ba5725e7cc;hpb=4e83acbf365d9b87dfdc95aef5c46785b33d2269;p=asdcplib.git diff --git a/src/KM_fileio.cpp b/src/KM_fileio.cpp index 4453e89..b9257ab 100644 --- a/src/KM_fileio.cpp +++ b/src/KM_fileio.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2004-2006, John Hurst +Copyright (c) 2004-2009, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,8 +32,13 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include + #include +#ifdef KM_WIN32 +#include +#endif + using namespace Kumu; #ifdef KM_WIN32 @@ -41,22 +46,58 @@ typedef struct _stati64 fstat_t; #define S_IFLNK 0 -// AFAIK, there is no iovec equivalent in the win32 API +// win32 has WriteFileGather() and ReadFileScatter() but they +// demand page alignment and page sizing, making them unsuitable +// for use with arbitrary buffer sizes. struct iovec { char* iov_base; // stupid iovec uses char* int iov_len; }; #else +# if defined(__linux__) +# include +# else +# include +# include +# endif + +#include #include typedef struct stat fstat_t; #endif +// +static void +split(const std::string& str, char separator, std::list& components) +{ + const char* pstr = str.c_str(); + const char* r = strchr(pstr, separator); + + while ( r != 0 ) + { + assert(r >= pstr); + if ( r > pstr ) + { + std::string tmp_str; + tmp_str.assign(pstr, (r - pstr)); + components.push_back(tmp_str); + } + + pstr = r + 1; + r = strchr(pstr, separator); + } + + if( strlen(pstr) > 0 ) + components.push_back(std::string(pstr)); +} + + // static Kumu::Result_t do_stat(const char* path, fstat_t* stat_info) { - KM_TEST_NULL_STR(path); - KM_TEST_NULL(stat_info); + KM_TEST_NULL_STR_L(path); + KM_TEST_NULL_L(stat_info); Kumu::Result_t result = Kumu::RESULT_OK; @@ -71,7 +112,7 @@ do_stat(const char* path, fstat_t* stat_info) if ( stat(path, stat_info) == -1L ) result = Kumu::RESULT_FILEOPEN; - if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 ) + if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 ) result = Kumu::RESULT_FILEOPEN; #endif @@ -82,16 +123,16 @@ do_stat(const char* path, fstat_t* stat_info) // static Kumu::Result_t -do_fstat(HANDLE handle, fstat_t* stat_info) +do_fstat(FileHandle handle, fstat_t* stat_info) { - KM_TEST_NULL(stat_info); + KM_TEST_NULL_L(stat_info); Kumu::Result_t result = Kumu::RESULT_OK; if ( fstat(handle, stat_info) == -1L ) result = Kumu::RESULT_FILEOPEN; - if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 ) + if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 ) result = Kumu::RESULT_FILEOPEN; return result; @@ -102,12 +143,29 @@ do_fstat(HANDLE handle, fstat_t* stat_info) // bool -Kumu::PathIsFile(const char* pathname) +Kumu::PathExists(const std::string& pathname) { - assert(pathname); + if ( pathname.empty() ) + return false; + fstat_t info; - if ( KM_SUCCESS(do_stat(pathname, &info)) ) + if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) ) + return true; + + return false; +} + +// +bool +Kumu::PathIsFile(const std::string& pathname) +{ + if ( pathname.empty() ) + return false; + + fstat_t info; + + if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) ) { if ( info.st_mode & ( S_IFREG|S_IFLNK ) ) return true; @@ -119,12 +177,14 @@ Kumu::PathIsFile(const char* pathname) // bool -Kumu::PathIsDirectory(const char* pathname) +Kumu::PathIsDirectory(const std::string& pathname) { - assert(pathname); + if ( pathname.empty() ) + return false; + fstat_t info; - if ( KM_SUCCESS(do_stat(pathname, &info)) ) + if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) ) { if ( info.st_mode & S_IFDIR ) return true; @@ -133,15 +193,16 @@ Kumu::PathIsDirectory(const char* pathname) return false; } - // Kumu::fsize_t -Kumu::FileSize(const char* pathname) +Kumu::FileSize(const std::string& pathname) { - assert(pathname); + if ( pathname.empty() ) + return 0; + fstat_t info; - if ( KM_SUCCESS(do_stat(pathname, &info)) ) + if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) ) { if ( info.st_mode & ( S_IFREG|S_IFLNK ) ) return(info.st_size); @@ -150,6 +211,368 @@ Kumu::FileSize(const char* pathname) return 0; } +// +static PathCompList_t& +s_PathMakeCanonical(PathCompList_t& CList, bool is_absolute) +{ + PathCompList_t::iterator ci, ri; // component and removal iterators + + for ( ci = CList.begin(); ci != CList.end(); ci++ ) + { + if ( *ci == "." && ( CList.size() > 1 || is_absolute ) ) + { + ri = ci++; + CList.erase(ri); + } + else if ( *ci == ".." && ci != CList.begin() ) + { + ri = ci; + ri--; + + if ( *ri != ".." ) + { + CList.erase(ri); + ri = ci++; + CList.erase(ri); + } + } + } + + return CList; +} + +// +std::string +Kumu::PathMakeCanonical(const std::string& Path, char separator) +{ + PathCompList_t CList; + bool is_absolute = PathIsAbsolute(Path, separator); + s_PathMakeCanonical(PathToComponents(Path, CList, separator), is_absolute); + + if ( is_absolute ) + return ComponentsToAbsolutePath(CList, separator); + + return ComponentsToPath(CList, separator); +} + +// +bool +Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs) +{ + return PathMakeCanonical(lhs) == PathMakeCanonical(rhs); +} + +// +Kumu::PathCompList_t& +Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator) +{ + split(Path, separator, CList); + return CList; +} + +// +std::string +Kumu::ComponentsToPath(const PathCompList_t& CList, char separator) +{ + if ( CList.empty() ) + return ""; + + PathCompList_t::const_iterator ci = CList.begin(); + std::string out_path = *ci; + + for ( ci++; ci != CList.end(); ci++ ) + out_path += separator + *ci; + + return out_path; +} + +// +std::string +Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator) +{ + std::string out_path; + + if ( CList.empty() ) + out_path = separator; + else + { + PathCompList_t::const_iterator ci; + + for ( ci = CList.begin(); ci != CList.end(); ci++ ) + out_path += separator + *ci; + } + + return out_path; +} + +// +bool +Kumu::PathHasComponents(const std::string& Path, char separator) +{ + if ( strchr(Path.c_str(), separator) == 0 ) + return false; + + return true; +} + +// +bool +Kumu::PathIsAbsolute(const std::string& Path, char separator) +{ + if ( Path.empty() ) + return false; + + if ( Path[0] == separator) + return true; + + return false; +} + +// +std::string +Kumu::PathMakeAbsolute(const std::string& Path, char separator) +{ + if ( Path.empty() ) + { + std::string out_path; + out_path = separator; + return out_path; + } + + if ( PathIsAbsolute(Path, separator) ) + return Path; + + char cwd_buf [MaxFilePath]; + if ( getcwd(cwd_buf, MaxFilePath) == 0 ) + { + DefaultLogSink().Error("Error retrieving current working directory."); + return ""; + } + + PathCompList_t CList; + PathToComponents(cwd_buf, CList); + CList.push_back(Path); + + return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, true), separator); +} + +// +std::string +Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent) +{ + size_t pos = Path.find(Parent); + + if ( pos == 0 ) // Parent found at offset 0 + return Path.substr(Parent.size()+1); + + return Path; +} + +// +std::string +Kumu::PathBasename(const std::string& Path, char separator) +{ + PathCompList_t CList; + PathToComponents(Path, CList, separator); + + if ( CList.empty() ) + return ""; + + return CList.back(); +} + +// +std::string +Kumu::PathDirname(const std::string& Path, char separator) +{ + PathCompList_t CList; + bool is_absolute = PathIsAbsolute(Path, separator); + PathToComponents(Path, CList, separator); + + if ( CList.empty() ) + return is_absolute ? "/" : ""; + + CList.pop_back(); + + if ( is_absolute ) + return ComponentsToAbsolutePath(CList, separator); + + return ComponentsToPath(CList, separator); +} + +// +std::string +Kumu::PathGetExtension(const std::string& Path) +{ + std::string Basename = PathBasename(Path); + const char* p = strrchr(Basename.c_str(), '.'); + + if ( p++ == 0 ) + return ""; + + return p; +} + +// +std::string +Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes +{ + std::string Basename = PathBasename(Path); + const char* p = strrchr(Basename.c_str(), '.'); + + if ( p != 0 ) + Basename = Basename.substr(0, p - Basename.c_str()); + + if ( Extension.empty() ) + return Basename; + + return Basename + "." + Extension; +} + +// +std::string +Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator) +{ + return Path1 + separator + Path2; +} + +// +std::string +Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator) +{ + return Path1 + separator + Path2 + separator + Path3; +} + +// +std::string +Kumu::PathJoin(const std::string& Path1, const std::string& Path2, + const std::string& Path3, const std::string& Path4, char separator) +{ + return Path1 + separator + Path2 + separator + Path3 + separator + Path4; +} + +// +Kumu::PathList_t& +Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths, + Kumu::PathList_t& FoundPaths, bool one_shot, char separator) +{ + PathList_t::const_iterator si; + for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ ) + { + FindInPath(Pattern, *si, FoundPaths, one_shot, separator); + + if ( one_shot && ! FoundPaths.empty() ) + break; + } + + return FoundPaths; +} + +// +Kumu::PathList_t& +Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir, + Kumu::PathList_t& FoundPaths, bool one_shot, char separator) +{ + char name_buf[MaxFilePath]; + DirScanner Dir; + + if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) ) + { + while ( KM_SUCCESS(Dir.GetNext(name_buf)) ) + { + if ( name_buf[0] == '.' ) continue; // no hidden files + std::string tmp_path = SearchDir + separator + name_buf; + + if ( PathIsDirectory(tmp_path.c_str()) ) + FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator); + + else if ( Pattern.Match(name_buf) ) + { + FoundPaths.push_back(SearchDir + separator + name_buf); + if ( one_shot ) + break; + } + } + } + + return FoundPaths; +} + + +#ifndef KM_WIN32 + +// +Kumu::PathMatchRegex::PathMatchRegex(const std::string& s) +{ + int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE)); + + if ( result ) + { + char buf[128]; + regerror(result, &m_regex, buf, 128); + DefaultLogSink().Error("PathMatchRegex: %s\n", buf); + regfree(&m_regex); + } +} + +Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() { + m_regex = rhs.m_regex; +} + +Kumu::PathMatchRegex::~PathMatchRegex() { + regfree(&m_regex); +} + +bool +Kumu::PathMatchRegex::Match(const std::string& s) const { + return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 ); +} + + + +// +Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob) +{ + std::string regex; // convert glob to regex + + for ( const char* p = glob.c_str(); *p != 0; p++ ) + { + switch (*p) + { + case '.': regex += "\\."; break; + case '*': regex += ".*"; break; + case '?': regex += ".?"; break; + default: regex += *p; + } + } + regex += '$'; + + int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB); + + if ( result ) + { + char buf[128]; + regerror(result, &m_regex, buf, 128); + DefaultLogSink().Error("PathMatchRegex: %s\n", buf); + regfree(&m_regex); + } +} + +Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() { + m_regex = rhs.m_regex; +} + +Kumu::PathMatchGlob::~PathMatchGlob() { + regfree(&m_regex); +} + +bool +Kumu::PathMatchGlob::Match(const std::string& s) const { + return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 ); +} + +#endif + //------------------------------------------------------------------------------------------ // portable aspects of the file classes @@ -196,13 +619,13 @@ Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len) { assert( ! m_IOVec.empty() ); register h__iovec* iov = m_IOVec; - KM_TEST_NULL(buf); + KM_TEST_NULL_L(buf); if ( iov->m_Count >= IOVecMaxEntries ) { - DefaultLogSink().Error("The iovec is full! Only %lu entries allowed before a flush.\n", + DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n", IOVecMaxEntries); - return RESULT_FAIL; + return RESULT_WRITEFAIL; } iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char* @@ -220,7 +643,7 @@ Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len) Kumu::Result_t Kumu::FileReader::OpenRead(const char* filename) const { - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); const_cast(this)->m_Filename = filename; // suppress popup window on error @@ -283,9 +706,9 @@ Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const Kumu::Result_t Kumu::FileReader::Tell(Kumu::fpos_t* pos) const { - KM_TEST_NULL(pos); + KM_TEST_NULL_L(pos); - if ( m_Handle == (HANDLE)-1L ) + if ( m_Handle == INVALID_HANDLE_VALUE ) return Kumu::RESULT_FILEOPEN; LARGE_INTEGER in; @@ -308,7 +731,7 @@ Kumu::FileReader::Tell(Kumu::fpos_t* pos) const Kumu::Result_t Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const { - KM_TEST_NULL(buf); + KM_TEST_NULL_L(buf); Result_t result = Kumu::RESULT_OK; DWORD tmp_count; ui32_t tmp_int; @@ -345,7 +768,7 @@ Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const Kumu::Result_t Kumu::FileWriter::OpenWrite(const char* filename) { - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); m_Filename = filename; // suppress popup window on error @@ -397,13 +820,12 @@ Kumu::FileWriter::Writev(ui32_t* bytes_written) (DWORD*)&tmp_count, NULL); - if ( wr_result == 0 ) + if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len) { result = Kumu::RESULT_WRITEFAIL; break; } - assert(iov->m_iovec[i].iov_len == tmp_count); *bytes_written += tmp_count; } @@ -417,7 +839,7 @@ Kumu::FileWriter::Writev(ui32_t* bytes_written) Kumu::Result_t Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written) { - KM_TEST_NULL(buf); + KM_TEST_NULL_L(buf); ui32_t tmp_int; if ( bytes_written == 0 ) @@ -431,7 +853,10 @@ Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL); ::SetErrorMode(prev); - return ( result == 0 ) ? Kumu::RESULT_WRITEFAIL : Kumu::RESULT_OK; + if ( result == 0 || *bytes_written != buf_len ) + return Kumu::RESULT_WRITEFAIL; + + return Kumu::RESULT_OK; } #else // KM_WIN32 @@ -442,7 +867,7 @@ Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written Kumu::Result_t Kumu::FileReader::OpenRead(const char* filename) const { - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); const_cast(this)->m_Filename = filename; const_cast(this)->m_Handle = open(filename, O_RDONLY, 0); return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK; @@ -477,7 +902,7 @@ Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const Kumu::Result_t Kumu::FileReader::Tell(Kumu::fpos_t* pos) const { - KM_TEST_NULL(pos); + KM_TEST_NULL_L(pos); if ( m_Handle == -1L ) return RESULT_FILEOPEN; @@ -495,7 +920,7 @@ Kumu::FileReader::Tell(Kumu::fpos_t* pos) const Kumu::Result_t Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const { - KM_TEST_NULL(buf); + KM_TEST_NULL_L(buf); i32_t tmp_count = 0; ui32_t tmp_int = 0; @@ -522,9 +947,9 @@ Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const Kumu::Result_t Kumu::FileWriter::OpenWrite(const char* filename) { - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); m_Filename = filename; - m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644); + m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664); if ( m_Handle == -1L ) { @@ -540,9 +965,9 @@ Kumu::FileWriter::OpenWrite(const char* filename) Kumu::Result_t Kumu::FileWriter::OpenModify(const char* filename) { - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); m_Filename = filename; - m_Handle = open(filename, O_RDWR|O_CREAT, 0644); + m_Handle = open(filename, O_RDWR|O_CREAT, 0664); if ( m_Handle == -1L ) { @@ -568,13 +993,17 @@ Kumu::FileWriter::Writev(ui32_t* bytes_written) if ( m_Handle == -1L ) return RESULT_STATE; - int read_size = writev(m_Handle, iov->m_iovec, iov->m_Count); + int total_size = 0; + for ( int i = 0; i < iov->m_Count; i++ ) + total_size += iov->m_iovec[i].iov_len; + + int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count); - if ( read_size == -1L ) + if ( write_size == -1L || write_size != total_size ) return RESULT_WRITEFAIL; iov->m_Count = 0; - *bytes_written = read_size; + *bytes_written = write_size; return RESULT_OK; } @@ -582,24 +1011,21 @@ Kumu::FileWriter::Writev(ui32_t* bytes_written) Kumu::Result_t Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written) { - KM_TEST_NULL(buf); + KM_TEST_NULL_L(buf); ui32_t tmp_int; if ( bytes_written == 0 ) bytes_written = &tmp_int; - // TODO: flush iovec - - if ( m_Handle == -1L ) return RESULT_STATE; - int read_size = write(m_Handle, buf, buf_len); - - if ( read_size == -1L ) + int write_size = write(m_Handle, buf, buf_len); + + if ( write_size == -1L || (ui32_t)write_size != buf_len ) return RESULT_WRITEFAIL; - *bytes_written = read_size; + *bytes_written = write_size; return RESULT_OK; } @@ -618,7 +1044,7 @@ Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t ma FileReader File; ByteString ReadBuf; - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); Result_t result = File.OpenRead(filename); @@ -627,7 +1053,16 @@ Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t ma fsize = File.Size(); if ( fsize > (Kumu::fpos_t)max_size ) - return RESULT_ALLOC; + { + DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size); + return RESULT_ALLOC; + } + + if ( fsize == 0 ) + { + DefaultLogSink().Error("%s: zero file size\n", filename); + return RESULT_READFAIL; + } result = ReadBuf.Capacity((ui32_t)fsize); } @@ -648,33 +1083,144 @@ Kumu::WriteStringIntoFile(const char* filename, const std::string& inString) { FileWriter File; ui32_t write_count = 0; - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); Result_t result = File.OpenWrite(filename); if ( KM_SUCCESS(result) ) result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count); - if ( KM_SUCCESS(result) && write_count != inString.length() ) - return RESULT_WRITEFAIL; + return result; +} - return RESULT_OK; +//------------------------------------------------------------------------------------------ + + +// +Kumu::Result_t +Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size) +{ + ByteString Buffer; + ui32_t file_size = static_cast(FileSize(Filename)); + Result_t result = Buffer.Capacity(file_size); + + if ( KM_SUCCESS(result) ) + { + ui32_t read_count = 0; + FileWriter Reader; + + result = Reader.OpenRead(Filename.c_str()); + + if ( KM_SUCCESS(result) ) + result = Reader.Read(Buffer.Data(), file_size, &read_count); + + if ( KM_SUCCESS(result) ) + { + assert(file_size == read_count); + Buffer.Length(read_count); + MemIOReader MemReader(&Buffer); + result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL; + } + } + + return result; +} + +// +Kumu::Result_t +Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename) +{ + ByteString Buffer; + Result_t result = Buffer.Capacity(Object.ArchiveLength()); + + if ( KM_SUCCESS(result) ) + { + ui32_t write_count = 0; + FileWriter Writer; + MemIOWriter MemWriter(&Buffer); + + result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL; + + if ( KM_SUCCESS(result) ) + { + Buffer.Length(MemWriter.Length()); + result = Writer.OpenWrite(Filename.c_str()); + } + + if ( KM_SUCCESS(result) ) + result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count); + } + + return result; +} + +//------------------------------------------------------------------------------------------ +// + +// +Result_t +Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size) +{ + ui32_t file_size = FileSize(Filename); + Result_t result = Buffer.Capacity(file_size); + + if ( KM_SUCCESS(result) ) + { + ui32_t read_count = 0; + FileWriter Reader; + + result = Reader.OpenRead(Filename.c_str()); + + if ( KM_SUCCESS(result) ) + result = Reader.Read(Buffer.Data(), file_size, &read_count); + + if ( KM_SUCCESS(result) ) + { + if ( file_size != read_count) + return RESULT_READFAIL; + + Buffer.Length(read_count); + } + } + + return result; } +// +Result_t +Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename) +{ + ui32_t write_count = 0; + FileWriter Writer; + + Result_t result = Writer.OpenWrite(Filename.c_str()); + + if ( KM_SUCCESS(result) ) + result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count); + + if ( KM_SUCCESS(result) && Buffer.Length() != write_count) + return RESULT_WRITEFAIL; + + return result; +} //------------------------------------------------------------------------------------------ // + // Win32 directory scanner // #ifdef KM_WIN32 +// +Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {} + // // Result_t Kumu::DirScanner::Open(const char* filename) { - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); // we need to append a '*' to read the entire directory ui32_t fn_len = strlen(filename); @@ -727,7 +1273,7 @@ Kumu::DirScanner::Close() Result_t Kumu::DirScanner::GetNext(char* filename) { - KM_TEST_NULL(filename); + KM_TEST_NULL_L(filename); if ( m_Handle == -1 ) return RESULT_FILEOPEN; @@ -754,21 +1300,36 @@ Kumu::DirScanner::GetNext(char* filename) // POSIX directory scanner +// +Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {} + // Result_t Kumu::DirScanner::Open(const char* filename) { - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); Result_t result = RESULT_OK; if ( ( m_Handle = opendir(filename) ) == NULL ) { - if ( errno == ENOENT ) - result = RESULT_ENDOFFILE; - - else - result = RESULT_FAIL; + switch ( errno ) + { + case ENOENT: + case ENOTDIR: + result = RESULT_NOTAFILE; + case EACCES: + result = RESULT_NO_PERM; + case ELOOP: + case ENAMETOOLONG: + result = RESULT_PARAM; + case EMFILE: + case ENFILE: + result = RESULT_STATE; + default: + DefaultLogSink().Error("DirScanner::Open(%s): %s\n", filename, strerror(errno)); + result = RESULT_FAIL; + } } return result; @@ -782,8 +1343,17 @@ Kumu::DirScanner::Close() if ( m_Handle == NULL ) return RESULT_FILEOPEN; - if ( closedir(m_Handle) == -1 ) - return RESULT_FAIL; + if ( closedir(m_Handle) == -1 ) { + switch ( errno ) + { + case EBADF: + case EINTR: + return RESULT_STATE; + default: + DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno)); + return RESULT_FAIL; + } + } m_Handle = NULL; return RESULT_OK; @@ -794,7 +1364,7 @@ Kumu::DirScanner::Close() Result_t Kumu::DirScanner::GetNext(char* filename) { - KM_TEST_NULL(filename); + KM_TEST_NULL_L(filename); if ( m_Handle == NULL ) return RESULT_FILEOPEN; @@ -817,6 +1387,191 @@ Kumu::DirScanner::GetNext(char* filename) #endif // KM_WIN32 +//------------------------------------------------------------------------------------------ + +// +// Attention Windows users: make sure to use the proper separator character +// with these functions. +// + +// given a path string, create any missing directories so that PathIsDirectory(Path) is true. +// +Result_t +Kumu::CreateDirectoriesInPath(const std::string& Path) +{ + bool abs = PathIsAbsolute(Path); + PathCompList_t PathComps, TmpPathComps; + + PathToComponents(Path, PathComps); + + while ( ! PathComps.empty() ) + { + TmpPathComps.push_back(PathComps.front()); + PathComps.pop_front(); + std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps); + + if ( ! PathIsDirectory(tmp_path) ) + { +#ifdef KM_WIN32 + if ( mkdir(tmp_path.c_str()) != 0 ) +#else // KM_WIN32 + if ( mkdir(tmp_path.c_str(), 0775) != 0 ) +#endif // KM_WIN32 + { + DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n", + tmp_path.c_str(), strerror(errno)); + return RESULT_DIR_CREATE; + } + } + } + + return RESULT_OK; +} + + +// +Result_t +Kumu::DeleteFile(const std::string& filename) +{ + if ( unlink(filename.c_str()) == 0 ) + return RESULT_OK; + + switch ( errno ) + { + case ENOENT: + case ENOTDIR: return RESULT_NOTAFILE; + + case EROFS: + case EBUSY: + case EACCES: + case EPERM: return RESULT_NO_PERM; + } + + DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno)); + return RESULT_FAIL; +} + +// +Result_t +h__DeletePath(const std::string& pathname) +{ + if ( pathname.empty() ) + return RESULT_NULL_STR; + + Result_t result = RESULT_OK; + + if ( ! PathIsDirectory(pathname) ) + { + result = DeleteFile(pathname); + } + else + { + { + DirScanner TestDir; + char next_file[Kumu::MaxFilePath]; + result = TestDir.Open(pathname.c_str()); + + while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) ) + { + if ( next_file[0] == '.' ) + { + if ( next_file[1] == 0 ) + continue; // don't delete 'this' + + if ( next_file[1] == '.' && next_file[2] == 0 ) + continue; // don't delete 'this' parent + } + + result = h__DeletePath(pathname + std::string("/") + next_file); + } + } + + if ( rmdir(pathname.c_str()) != 0 ) + { + switch ( errno ) + { + case ENOENT: + case ENOTDIR: + result = RESULT_NOTAFILE; + break; + + case EROFS: + case EBUSY: + case EACCES: + case EPERM: + result = RESULT_NO_PERM; + break; + + default: + DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno)); + result = RESULT_FAIL; + } + } + } + + return result; +} + +// +Result_t +Kumu::DeletePath(const std::string& pathname) +{ + std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname)); + DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str()); + return h__DeletePath(c_pathname); +} + + +//------------------------------------------------------------------------------------------ +// + + +Result_t +Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space) +{ +#ifdef KM_WIN32 + ULARGE_INTEGER lTotalNumberOfBytes; + ULARGE_INTEGER lTotalNumberOfFreeBytes; + + BOOL fResult = ::GetDiskFreeSpaceEx(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes); + if (fResult) { + free_space = static_cast(lTotalNumberOfFreeBytes.QuadPart); + total_space = static_cast(lTotalNumberOfBytes.QuadPart); + return RESULT_OK; + } + HRESULT LastError = ::GetLastError(); + + DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError()); + return RESULT_FAIL; +#else // KM_WIN32 + struct statfs s; + + if ( statfs(path.c_str(), &s) == 0 ) + { + if ( s.f_blocks < 1 ) + { + DefaultLogSink().Error("File system %s has impossible size: %ld\n", + path.c_str(), s.f_blocks); + return RESULT_FAIL; + } + + free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail; + total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks; + return RESULT_OK; + } + + switch ( errno ) + { + case ENOENT: + case ENOTDIR: return RESULT_NOTAFILE; + case EACCES: return RESULT_NO_PERM; + } + + DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno)); + return RESULT_FAIL; +#endif // KM_WIN32 +} + // // end KM_fileio.cpp