X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2FKM_fileio.cpp;h=5a56c44cd09890c8831f4f4500e59c9e651f8844;hb=44bc524e3cf74f0903b7fbb51ca2f366f48d3495;hp=464d47fd005a4b389350aecdaaa3dfc04e48440a;hpb=5e91ca52284adc91a42d6fe389c9cc70a33126a6;p=asdcplib.git diff --git a/src/KM_fileio.cpp b/src/KM_fileio.cpp index 464d47f..5a56c44 100644 --- a/src/KM_fileio.cpp +++ b/src/KM_fileio.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2004-2006, John Hurst +Copyright (c) 2004-2007, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -33,6 +33,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#ifdef KM_WIN32 +#include +#endif using namespace Kumu; @@ -41,7 +44,9 @@ 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; @@ -51,12 +56,39 @@ struct iovec { 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; + assert(r - pstr < 100); + 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; @@ -84,7 +116,7 @@ do_stat(const char* path, fstat_t* stat_info) static Kumu::Result_t do_fstat(HANDLE handle, fstat_t* stat_info) { - KM_TEST_NULL(stat_info); + KM_TEST_NULL_L(stat_info); Kumu::Result_t result = Kumu::RESULT_OK; @@ -102,12 +134,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 +168,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 +184,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 +202,346 @@ Kumu::FileSize(const char* pathname) return 0; } +// +static PathCompList_t& +s_PathMakeCanonical(PathCompList_t& CList, char separator, 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), 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; + CList.push_back(cwd_buf); + CList.push_back(Path); + + return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, separator, 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; +} + +// +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) { + 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) { + 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 +588,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 %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 +612,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,7 +675,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 == (HANDLE)-1L ) return Kumu::RESULT_FILEOPEN; @@ -308,7 +700,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 +737,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 +789,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 +808,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 +822,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 +836,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 +871,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 +889,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 +916,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 +934,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 +962,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 +980,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 +1013,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 +1022,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,17 +1052,14 @@ 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_OK; + return result; } @@ -674,7 +1075,7 @@ Kumu::WriteStringIntoFile(const char* filename, const std::string& inString) 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 +1128,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; @@ -758,7 +1159,7 @@ Kumu::DirScanner::GetNext(char* filename) Result_t Kumu::DirScanner::Open(const char* filename) { - KM_TEST_NULL_STR(filename); + KM_TEST_NULL_STR_L(filename); Result_t result = RESULT_OK; @@ -794,7 +1195,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;