2 Copyright (c) 2004-2007, 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>
43 typedef struct _stati64 fstat_t;
47 // win32 has WriteFileGather() and ReadFileScatter() but they
48 // demand page alignment and page sizing, making them unsuitable
49 // for use with arbitrary buffer sizes.
51 char* iov_base; // stupid iovec uses char*
56 typedef struct stat fstat_t;
61 split(const std::string& str, char separator, std::list<std::string>& components)
63 const char* pstr = str.c_str();
64 const char* r = strchr(pstr, separator);
72 assert(r - pstr < 100);
73 tmp_str.assign(pstr, (r - pstr));
74 components.push_back(tmp_str);
78 r = strchr(pstr, separator);
81 if( strlen(pstr) > 0 )
82 components.push_back(std::string(pstr));
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);
98 if ( _stati64(path, stat_info) == (__int64)-1 )
99 result = Kumu::RESULT_FILEOPEN;
101 ::SetErrorMode( prev );
103 if ( stat(path, stat_info) == -1L )
104 result = Kumu::RESULT_FILEOPEN;
106 if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
107 result = Kumu::RESULT_FILEOPEN;
116 static Kumu::Result_t
117 do_fstat(HANDLE handle, fstat_t* stat_info)
119 KM_TEST_NULL_L(stat_info);
121 Kumu::Result_t result = Kumu::RESULT_OK;
123 if ( fstat(handle, stat_info) == -1L )
124 result = Kumu::RESULT_FILEOPEN;
126 if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
127 result = Kumu::RESULT_FILEOPEN;
137 Kumu::PathExists(const std::string& pathname)
139 if ( pathname.empty() )
144 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
152 Kumu::PathIsFile(const std::string& pathname)
154 if ( pathname.empty() )
159 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
161 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
171 Kumu::PathIsDirectory(const std::string& pathname)
173 if ( pathname.empty() )
178 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
180 if ( info.st_mode & S_IFDIR )
189 Kumu::FileSize(const std::string& pathname)
191 if ( pathname.empty() )
196 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
198 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
199 return(info.st_size);
206 static PathCompList_t&
207 s_PathMakeCanonical(PathCompList_t& CList, char separator, bool is_absolute)
209 PathCompList_t::iterator ci, ri; // component and removal iterators
211 for ( ci = CList.begin(); ci != CList.end(); ci++ )
213 if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
218 else if ( *ci == ".." && ci != CList.begin() )
237 Kumu::PathMakeCanonical(const std::string& Path, char separator)
239 PathCompList_t CList;
240 bool is_absolute = PathIsAbsolute(Path, separator);
241 s_PathMakeCanonical(PathToComponents(Path, CList, separator), separator, is_absolute);
244 return ComponentsToAbsolutePath(CList, separator);
246 return ComponentsToPath(CList, separator);
251 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
253 return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
257 Kumu::PathCompList_t&
258 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
260 split(Path, separator, CList);
266 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
271 PathCompList_t::const_iterator ci = CList.begin();
272 std::string out_path = *ci;
274 for ( ci++; ci != CList.end(); ci++ )
275 out_path += separator + *ci;
282 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
284 std::string out_path;
287 out_path = separator;
290 PathCompList_t::const_iterator ci;
292 for ( ci = CList.begin(); ci != CList.end(); ci++ )
293 out_path += separator + *ci;
301 Kumu::PathHasComponents(const std::string& Path, char separator)
303 if ( strchr(Path.c_str(), separator) == 0 )
311 Kumu::PathIsAbsolute(const std::string& Path, char separator)
316 if ( Path[0] == separator)
324 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
328 std::string out_path;
329 out_path = separator;
333 if ( PathIsAbsolute(Path, separator) )
336 char cwd_buf [MaxFilePath];
337 if ( getcwd(cwd_buf, MaxFilePath) == 0 )
339 DefaultLogSink().Error("Error retrieving current working directory.");
343 PathCompList_t CList;
344 CList.push_back(cwd_buf);
345 CList.push_back(Path);
347 return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, separator, true), separator);
352 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
354 size_t pos = Path.find(Parent);
356 if ( pos == 0 ) // Parent found at offset 0
357 return Path.substr(Parent.size()+1);
364 Kumu::PathBasename(const std::string& Path, char separator)
366 PathCompList_t CList;
367 PathToComponents(Path, CList, separator);
377 Kumu::PathDirname(const std::string& Path, char separator)
379 PathCompList_t CList;
380 bool is_absolute = PathIsAbsolute(Path, separator);
381 PathToComponents(Path, CList, separator);
384 return is_absolute ? "/" : "";
389 return ComponentsToAbsolutePath(CList, separator);
391 return ComponentsToPath(CList, separator);
396 Kumu::PathGetExtension(const std::string& Path)
398 std::string Basename = PathBasename(Path);
399 const char* p = strrchr(Basename.c_str(), '.');
409 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
411 std::string Basename = PathBasename(Path);
412 const char* p = strrchr(Basename.c_str(), '.');
415 Basename = Basename.substr(0, p - Basename.c_str());
417 if ( Extension.empty() )
420 return Basename + "." + Extension;
425 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
426 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
428 PathList_t::const_iterator si;
429 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
431 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
433 if ( one_shot && ! FoundPaths.empty() )
442 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
443 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
445 char name_buf[MaxFilePath];
448 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
450 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
452 if ( name_buf[0] == '.' ) continue; // no hidden files
453 std::string tmp_path = SearchDir + separator + name_buf;
455 if ( PathIsDirectory(tmp_path.c_str()) )
456 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
458 else if ( Pattern.Match(name_buf) )
460 FoundPaths.push_back(SearchDir + separator + name_buf);
474 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
476 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
481 regerror(result, &m_regex, buf, 128);
482 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
487 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) {
488 m_regex = rhs.m_regex;
491 Kumu::PathMatchRegex::~PathMatchRegex() {
496 Kumu::PathMatchRegex::Match(const std::string& s) const {
497 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
503 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
505 std::string regex; // convert glob to regex
507 for ( const char* p = glob.c_str(); *p != 0; p++ )
511 case '.': regex += "\\."; break;
512 case '*': regex += ".*"; break;
513 case '?': regex += ".?"; break;
514 default: regex += *p;
519 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
524 regerror(result, &m_regex, buf, 128);
525 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
530 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) {
531 m_regex = rhs.m_regex;
534 Kumu::PathMatchGlob::~PathMatchGlob() {
539 Kumu::PathMatchGlob::Match(const std::string& s) const {
540 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
545 //------------------------------------------------------------------------------------------
546 // portable aspects of the file classes
548 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
551 class Kumu::FileWriter::h__iovec
555 struct iovec m_iovec[IOVecMaxEntries];
556 h__iovec() : m_Count(0) {}
563 Kumu::FileReader::Size() const
566 return FileSize(m_Filename.c_str());
570 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
572 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
573 return(info.st_size);
580 // these are declared here instead of in the header file
581 // because we have a mem_ptr that is managing a hidden class
582 Kumu::FileWriter::FileWriter() {}
583 Kumu::FileWriter::~FileWriter() {}
587 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
589 assert( ! m_IOVec.empty() );
590 register h__iovec* iov = m_IOVec;
593 if ( iov->m_Count >= IOVecMaxEntries )
595 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
597 return RESULT_WRITEFAIL;
600 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
601 iov->m_iovec[iov->m_Count].iov_len = buf_len;
609 //------------------------------------------------------------------------------------------
613 Kumu::FileReader::OpenRead(const char* filename) const
615 KM_TEST_NULL_STR_L(filename);
616 const_cast<FileReader*>(this)->m_Filename = filename;
618 // suppress popup window on error
619 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
621 const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
622 (GENERIC_READ), // open for reading
623 FILE_SHARE_READ, // share for reading
625 OPEN_EXISTING, // read
626 FILE_ATTRIBUTE_NORMAL, // normal file
627 NULL // no template file
630 ::SetErrorMode(prev);
632 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
633 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
638 Kumu::FileReader::Close() const
640 if ( m_Handle == INVALID_HANDLE_VALUE )
641 return Kumu::RESULT_FILEOPEN;
643 // suppress popup window on error
644 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
645 BOOL result = ::CloseHandle(m_Handle);
646 ::SetErrorMode(prev);
647 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
649 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
654 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
656 if ( m_Handle == INVALID_HANDLE_VALUE )
657 return Kumu::RESULT_STATE;
660 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
661 in.QuadPart = position;
662 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
663 HRESULT LastError = GetLastError();
664 ::SetErrorMode(prev);
666 if ( (LastError != NO_ERROR
667 && (in.LowPart == INVALID_SET_FILE_POINTER
668 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
669 return Kumu::RESULT_READFAIL;
671 return Kumu::RESULT_OK;
676 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
680 if ( m_Handle == (HANDLE)-1L )
681 return Kumu::RESULT_FILEOPEN;
684 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
685 in.QuadPart = (__int64)0;
686 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
687 HRESULT LastError = GetLastError();
688 ::SetErrorMode(prev);
690 if ( (LastError != NO_ERROR
691 && (in.LowPart == INVALID_SET_FILE_POINTER
692 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
693 return Kumu::RESULT_READFAIL;
695 *pos = (Kumu::fpos_t)in.QuadPart;
696 return Kumu::RESULT_OK;
701 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
704 Result_t result = Kumu::RESULT_OK;
708 if ( read_count == 0 )
709 read_count = &tmp_int;
713 if ( m_Handle == INVALID_HANDLE_VALUE )
714 return Kumu::RESULT_FILEOPEN;
716 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
717 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
718 result = Kumu::RESULT_READFAIL;
720 ::SetErrorMode(prev);
722 if ( tmp_count == 0 ) /* EOF */
723 result = Kumu::RESULT_ENDOFFILE;
725 if ( KM_SUCCESS(result) )
726 *read_count = tmp_count;
733 //------------------------------------------------------------------------------------------
738 Kumu::FileWriter::OpenWrite(const char* filename)
740 KM_TEST_NULL_STR_L(filename);
741 m_Filename = filename;
743 // suppress popup window on error
744 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
746 m_Handle = ::CreateFile(filename,
747 (GENERIC_WRITE|GENERIC_READ), // open for reading
748 FILE_SHARE_READ, // share for reading
750 CREATE_ALWAYS, // overwrite (beware!)
751 FILE_ATTRIBUTE_NORMAL, // normal file
752 NULL // no template file
755 ::SetErrorMode(prev);
757 if ( m_Handle == INVALID_HANDLE_VALUE )
758 return Kumu::RESULT_FILEOPEN;
760 m_IOVec = new h__iovec;
761 return Kumu::RESULT_OK;
766 Kumu::FileWriter::Writev(ui32_t* bytes_written)
768 assert( ! m_IOVec.empty() );
769 register h__iovec* iov = m_IOVec;
772 if ( bytes_written == 0 )
773 bytes_written = &tmp_int;
775 if ( m_Handle == INVALID_HANDLE_VALUE )
776 return Kumu::RESULT_STATE;
779 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
780 Result_t result = Kumu::RESULT_OK;
782 // AFAIK, there is no writev() equivalent in the win32 API
783 for ( register int i = 0; i < iov->m_Count; i++ )
785 ui32_t tmp_count = 0;
786 BOOL wr_result = ::WriteFile(m_Handle,
787 iov->m_iovec[i].iov_base,
788 iov->m_iovec[i].iov_len,
792 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
794 result = Kumu::RESULT_WRITEFAIL;
798 *bytes_written += tmp_count;
801 ::SetErrorMode(prev);
802 iov->m_Count = 0; // error nor not, all is lost
809 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
814 if ( bytes_written == 0 )
815 bytes_written = &tmp_int;
817 if ( m_Handle == INVALID_HANDLE_VALUE )
818 return Kumu::RESULT_STATE;
820 // suppress popup window on error
821 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
822 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
823 ::SetErrorMode(prev);
825 if ( result == 0 || *bytes_written != buf_len )
826 return Kumu::RESULT_WRITEFAIL;
828 return Kumu::RESULT_OK;
832 //------------------------------------------------------------------------------------------
837 Kumu::FileReader::OpenRead(const char* filename) const
839 KM_TEST_NULL_STR_L(filename);
840 const_cast<FileReader*>(this)->m_Filename = filename;
841 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
842 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
847 Kumu::FileReader::Close() const
849 if ( m_Handle == -1L )
850 return RESULT_FILEOPEN;
853 const_cast<FileReader*>(this)->m_Handle = -1L;
859 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
861 if ( m_Handle == -1L )
862 return RESULT_FILEOPEN;
864 if ( lseek(m_Handle, position, whence) == -1L )
865 return RESULT_BADSEEK;
872 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
876 if ( m_Handle == -1L )
877 return RESULT_FILEOPEN;
879 Kumu::fpos_t tmp_pos;
881 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
882 return RESULT_READFAIL;
890 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
896 if ( read_count == 0 )
897 read_count = &tmp_int;
901 if ( m_Handle == -1L )
902 return RESULT_FILEOPEN;
904 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
905 return RESULT_READFAIL;
907 *read_count = tmp_count;
908 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
912 //------------------------------------------------------------------------------------------
917 Kumu::FileWriter::OpenWrite(const char* filename)
919 KM_TEST_NULL_STR_L(filename);
920 m_Filename = filename;
921 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
923 if ( m_Handle == -1L )
925 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
926 return RESULT_FILEOPEN;
929 m_IOVec = new h__iovec;
935 Kumu::FileWriter::OpenModify(const char* filename)
937 KM_TEST_NULL_STR_L(filename);
938 m_Filename = filename;
939 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
941 if ( m_Handle == -1L )
943 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
944 return RESULT_FILEOPEN;
947 m_IOVec = new h__iovec;
953 Kumu::FileWriter::Writev(ui32_t* bytes_written)
955 assert( ! m_IOVec.empty() );
956 register h__iovec* iov = m_IOVec;
959 if ( bytes_written == 0 )
960 bytes_written = &tmp_int;
962 if ( m_Handle == -1L )
966 for ( int i = 0; i < iov->m_Count; i++ )
967 total_size += iov->m_iovec[i].iov_len;
969 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
971 if ( write_size == -1L || write_size != total_size )
972 return RESULT_WRITEFAIL;
975 *bytes_written = write_size;
981 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
986 if ( bytes_written == 0 )
987 bytes_written = &tmp_int;
989 if ( m_Handle == -1L )
992 int write_size = write(m_Handle, buf, buf_len);
994 if ( write_size == -1L || (ui32_t)write_size != buf_len )
995 return RESULT_WRITEFAIL;
997 *bytes_written = write_size;
1004 //------------------------------------------------------------------------------------------
1009 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1012 ui32_t read_size = 0;
1016 KM_TEST_NULL_STR_L(filename);
1018 Result_t result = File.OpenRead(filename);
1020 if ( KM_SUCCESS(result) )
1022 fsize = File.Size();
1024 if ( fsize > (Kumu::fpos_t)max_size )
1026 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1027 return RESULT_ALLOC;
1032 DefaultLogSink().Error("%s: zero file size\n", filename);
1033 return RESULT_READFAIL;
1036 result = ReadBuf.Capacity((ui32_t)fsize);
1039 if ( KM_SUCCESS(result) )
1040 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1042 if ( KM_SUCCESS(result) )
1043 outString.assign((const char*)ReadBuf.RoData(), read_size);
1051 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1054 ui32_t write_count = 0;
1055 KM_TEST_NULL_STR_L(filename);
1057 Result_t result = File.OpenWrite(filename);
1059 if ( KM_SUCCESS(result) )
1060 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1066 //------------------------------------------------------------------------------------------
1069 // Win32 directory scanner
1076 Kumu::DirScanner::Open(const char* filename)
1078 KM_TEST_NULL_STR_L(filename);
1080 // we need to append a '*' to read the entire directory
1081 ui32_t fn_len = strlen(filename);
1082 char* tmp_file = (char*)malloc(fn_len + 8);
1084 if ( tmp_file == 0 )
1085 return RESULT_ALLOC;
1087 strcpy(tmp_file, filename);
1088 char* p = &tmp_file[fn_len] - 1;
1090 if ( *p != '/' && *p != '\\' )
1100 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1101 Result_t result = RESULT_OK;
1103 if ( m_Handle == -1 )
1104 result = RESULT_NOT_FOUND;
1113 Kumu::DirScanner::Close()
1115 if ( m_Handle == -1 )
1116 return RESULT_FILEOPEN;
1118 if ( _findclose((long)m_Handle) == -1 )
1126 // This sets filename param to the same per-instance buffer every time, so
1127 // the value will change on the next call
1129 Kumu::DirScanner::GetNext(char* filename)
1131 KM_TEST_NULL_L(filename);
1133 if ( m_Handle == -1 )
1134 return RESULT_FILEOPEN;
1136 if ( m_FileInfo.name[0] == '\0' )
1137 return RESULT_ENDOFFILE;
1139 strncpy(filename, m_FileInfo.name, MaxFilePath);
1140 Result_t result = RESULT_OK;
1142 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1144 m_FileInfo.name[0] = '\0';
1146 if ( errno != ENOENT )
1147 result = RESULT_FAIL;
1156 // POSIX directory scanner
1160 Kumu::DirScanner::Open(const char* filename)
1162 KM_TEST_NULL_STR_L(filename);
1164 Result_t result = RESULT_OK;
1166 if ( ( m_Handle = opendir(filename) ) == NULL )
1168 if ( errno == ENOENT )
1169 result = RESULT_ENDOFFILE;
1172 result = RESULT_FAIL;
1181 Kumu::DirScanner::Close()
1183 if ( m_Handle == NULL )
1184 return RESULT_FILEOPEN;
1186 if ( closedir(m_Handle) == -1 )
1196 Kumu::DirScanner::GetNext(char* filename)
1198 KM_TEST_NULL_L(filename);
1200 if ( m_Handle == NULL )
1201 return RESULT_FILEOPEN;
1203 struct dirent* entry;
1207 if ( ( entry = readdir(m_Handle)) == NULL )
1208 return RESULT_ENDOFFILE;
1213 strncpy(filename, entry->d_name, MaxFilePath);
1223 // end KM_fileio.cpp