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>
40 typedef struct _stati64 fstat_t;
44 // win32 has WriteFileGather() and ReadFileScatter() but they
45 // demand page alignment and page sizing, making them unsuitable
46 // for use with arbitrary buffer sizes.
48 char* iov_base; // stupid iovec uses char*
53 typedef struct stat fstat_t;
58 split(const std::string& str, char separator, std::list<std::string>& components)
60 const char* pstr = str.c_str();
61 const char* r = strchr(pstr, separator);
69 assert(r - pstr < 100);
70 tmp_str.assign(pstr, (r - pstr));
71 components.push_back(tmp_str);
75 r = strchr(pstr, separator);
78 if( strlen(pstr) > 0 )
79 components.push_back(std::string(pstr));
85 do_stat(const char* path, fstat_t* stat_info)
87 KM_TEST_NULL_STR_L(path);
88 KM_TEST_NULL_L(stat_info);
90 Kumu::Result_t result = Kumu::RESULT_OK;
93 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
95 if ( _stati64(path, stat_info) == (__int64)-1 )
96 result = Kumu::RESULT_FILEOPEN;
98 ::SetErrorMode( prev );
100 if ( stat(path, stat_info) == -1L )
101 result = Kumu::RESULT_FILEOPEN;
103 if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
104 result = Kumu::RESULT_FILEOPEN;
113 static Kumu::Result_t
114 do_fstat(HANDLE handle, fstat_t* stat_info)
116 KM_TEST_NULL_L(stat_info);
118 Kumu::Result_t result = Kumu::RESULT_OK;
120 if ( fstat(handle, stat_info) == -1L )
121 result = Kumu::RESULT_FILEOPEN;
123 if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
124 result = Kumu::RESULT_FILEOPEN;
134 Kumu::PathExists(const std::string& pathname)
136 if ( pathname.empty() )
141 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
149 Kumu::PathIsFile(const std::string& pathname)
151 if ( pathname.empty() )
156 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
158 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
168 Kumu::PathIsDirectory(const std::string& pathname)
170 if ( pathname.empty() )
175 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
177 if ( info.st_mode & S_IFDIR )
186 Kumu::FileSize(const std::string& pathname)
188 if ( pathname.empty() )
193 if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
195 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
196 return(info.st_size);
203 static PathCompList_t&
204 s_PathMakeCanonical(PathCompList_t& CList, char separator, bool is_absolute)
206 PathCompList_t::iterator ci, ri; // component and removal iterators
208 for ( ci = CList.begin(); ci != CList.end(); ci++ )
210 if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
215 else if ( *ci == ".." && ci != CList.begin() )
234 Kumu::PathMakeCanonical(const std::string& Path, char separator)
236 PathCompList_t CList;
237 bool is_absolute = PathIsAbsolute(Path, separator);
238 s_PathMakeCanonical(PathToComponents(Path, CList, separator), separator, is_absolute);
241 return ComponentsToAbsolutePath(CList, separator);
243 return ComponentsToPath(CList, separator);
248 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
250 return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
254 Kumu::PathCompList_t&
255 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
257 split(Path, separator, CList);
263 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
268 PathCompList_t::const_iterator ci = CList.begin();
269 std::string out_path = *ci;
271 for ( ci++; ci != CList.end(); ci++ )
272 out_path += separator + *ci;
279 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
281 std::string out_path;
284 out_path = separator;
287 PathCompList_t::const_iterator ci;
289 for ( ci = CList.begin(); ci != CList.end(); ci++ )
290 out_path += separator + *ci;
298 Kumu::PathHasComponents(const std::string& Path, char separator)
300 if ( strchr(Path.c_str(), separator) == 0 )
308 Kumu::PathIsAbsolute(const std::string& Path, char separator)
313 if ( Path[0] == separator)
321 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
325 std::string out_path;
326 out_path = separator;
330 if ( PathIsAbsolute(Path, separator) )
333 char cwd_buf [MaxFilePath];
334 if ( getcwd(cwd_buf, MaxFilePath) == 0 )
336 DefaultLogSink().Error("Error retrieving current working directory.");
340 PathCompList_t CList;
341 CList.push_back(cwd_buf);
342 CList.push_back(Path);
344 return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, separator, true), separator);
349 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
351 size_t pos = Path.find(Parent);
353 if ( pos == 0 ) // Parent found at offset 0
354 return Path.substr(Parent.size()+1);
361 Kumu::PathBasename(const std::string& Path, char separator)
363 PathCompList_t CList;
364 PathToComponents(Path, CList, separator);
374 Kumu::PathDirname(const std::string& Path, char separator)
376 PathCompList_t CList;
377 bool is_absolute = PathIsAbsolute(Path, separator);
378 PathToComponents(Path, CList, separator);
381 return is_absolute ? "/" : "";
386 return ComponentsToAbsolutePath(CList, separator);
388 return ComponentsToPath(CList, separator);
393 Kumu::PathGetExtension(const std::string& Path)
395 std::string Basename = PathBasename(Path);
396 const char* p = strrchr(Basename.c_str(), '.');
406 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
408 std::string Basename = PathBasename(Path);
409 const char* p = strrchr(Basename.c_str(), '.');
412 Basename = Basename.substr(0, p - Basename.c_str());
414 if ( Extension.empty() )
417 return Basename + "." + Extension;
422 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
423 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
425 PathList_t::const_iterator si;
426 for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
428 FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
430 if ( one_shot && ! FoundPaths.empty() )
439 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
440 Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
442 char name_buf[MaxFilePath];
445 if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
447 while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
449 if ( name_buf[0] == '.' ) continue; // no hidden files
450 std::string tmp_path = SearchDir + separator + name_buf;
452 if ( PathIsDirectory(tmp_path.c_str()) )
453 FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
455 else if ( Pattern.Match(name_buf) )
457 FoundPaths.push_back(SearchDir + separator + name_buf);
471 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
473 int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
478 regerror(result, &m_regex, buf, 128);
479 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
484 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) {
485 m_regex = rhs.m_regex;
488 Kumu::PathMatchRegex::~PathMatchRegex() {
493 Kumu::PathMatchRegex::Match(const std::string& s) const {
494 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
500 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
502 std::string regex; // convert glob to regex
504 for ( const char* p = glob.c_str(); *p != 0; p++ )
508 case '.': regex += "\\."; break;
509 case '*': regex += ".*"; break;
510 case '?': regex += ".?"; break;
511 default: regex += *p;
516 int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
521 regerror(result, &m_regex, buf, 128);
522 DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
527 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) {
528 m_regex = rhs.m_regex;
531 Kumu::PathMatchGlob::~PathMatchGlob() {
536 Kumu::PathMatchGlob::Match(const std::string& s) const {
537 return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
542 //------------------------------------------------------------------------------------------
543 // portable aspects of the file classes
545 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
548 class Kumu::FileWriter::h__iovec
552 struct iovec m_iovec[IOVecMaxEntries];
553 h__iovec() : m_Count(0) {}
560 Kumu::FileReader::Size() const
563 return FileSize(m_Filename.c_str());
567 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
569 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
570 return(info.st_size);
577 // these are declared here instead of in the header file
578 // because we have a mem_ptr that is managing a hidden class
579 Kumu::FileWriter::FileWriter() {}
580 Kumu::FileWriter::~FileWriter() {}
584 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
586 assert( ! m_IOVec.empty() );
587 register h__iovec* iov = m_IOVec;
590 if ( iov->m_Count >= IOVecMaxEntries )
592 DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
594 return RESULT_WRITEFAIL;
597 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
598 iov->m_iovec[iov->m_Count].iov_len = buf_len;
606 //------------------------------------------------------------------------------------------
610 Kumu::FileReader::OpenRead(const char* filename) const
612 KM_TEST_NULL_STR_L(filename);
613 const_cast<FileReader*>(this)->m_Filename = filename;
615 // suppress popup window on error
616 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
618 const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
619 (GENERIC_READ), // open for reading
620 FILE_SHARE_READ, // share for reading
622 OPEN_EXISTING, // read
623 FILE_ATTRIBUTE_NORMAL, // normal file
624 NULL // no template file
627 ::SetErrorMode(prev);
629 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
630 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
635 Kumu::FileReader::Close() const
637 if ( m_Handle == INVALID_HANDLE_VALUE )
638 return Kumu::RESULT_FILEOPEN;
640 // suppress popup window on error
641 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
642 BOOL result = ::CloseHandle(m_Handle);
643 ::SetErrorMode(prev);
644 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
646 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
651 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
653 if ( m_Handle == INVALID_HANDLE_VALUE )
654 return Kumu::RESULT_STATE;
657 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
658 in.QuadPart = position;
659 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
660 HRESULT LastError = GetLastError();
661 ::SetErrorMode(prev);
663 if ( (LastError != NO_ERROR
664 && (in.LowPart == INVALID_SET_FILE_POINTER
665 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
666 return Kumu::RESULT_READFAIL;
668 return Kumu::RESULT_OK;
673 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
677 if ( m_Handle == (HANDLE)-1L )
678 return Kumu::RESULT_FILEOPEN;
681 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
682 in.QuadPart = (__int64)0;
683 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
684 HRESULT LastError = GetLastError();
685 ::SetErrorMode(prev);
687 if ( (LastError != NO_ERROR
688 && (in.LowPart == INVALID_SET_FILE_POINTER
689 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
690 return Kumu::RESULT_READFAIL;
692 *pos = (Kumu::fpos_t)in.QuadPart;
693 return Kumu::RESULT_OK;
698 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
701 Result_t result = Kumu::RESULT_OK;
705 if ( read_count == 0 )
706 read_count = &tmp_int;
710 if ( m_Handle == INVALID_HANDLE_VALUE )
711 return Kumu::RESULT_FILEOPEN;
713 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
714 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
715 result = Kumu::RESULT_READFAIL;
717 ::SetErrorMode(prev);
719 if ( tmp_count == 0 ) /* EOF */
720 result = Kumu::RESULT_ENDOFFILE;
722 if ( KM_SUCCESS(result) )
723 *read_count = tmp_count;
730 //------------------------------------------------------------------------------------------
735 Kumu::FileWriter::OpenWrite(const char* filename)
737 KM_TEST_NULL_STR_L(filename);
738 m_Filename = filename;
740 // suppress popup window on error
741 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
743 m_Handle = ::CreateFile(filename,
744 (GENERIC_WRITE|GENERIC_READ), // open for reading
745 FILE_SHARE_READ, // share for reading
747 CREATE_ALWAYS, // overwrite (beware!)
748 FILE_ATTRIBUTE_NORMAL, // normal file
749 NULL // no template file
752 ::SetErrorMode(prev);
754 if ( m_Handle == INVALID_HANDLE_VALUE )
755 return Kumu::RESULT_FILEOPEN;
757 m_IOVec = new h__iovec;
758 return Kumu::RESULT_OK;
763 Kumu::FileWriter::Writev(ui32_t* bytes_written)
765 assert( ! m_IOVec.empty() );
766 register h__iovec* iov = m_IOVec;
769 if ( bytes_written == 0 )
770 bytes_written = &tmp_int;
772 if ( m_Handle == INVALID_HANDLE_VALUE )
773 return Kumu::RESULT_STATE;
776 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
777 Result_t result = Kumu::RESULT_OK;
779 // AFAIK, there is no writev() equivalent in the win32 API
780 for ( register int i = 0; i < iov->m_Count; i++ )
782 ui32_t tmp_count = 0;
783 BOOL wr_result = ::WriteFile(m_Handle,
784 iov->m_iovec[i].iov_base,
785 iov->m_iovec[i].iov_len,
789 if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
791 result = Kumu::RESULT_WRITEFAIL;
795 *bytes_written += tmp_count;
798 ::SetErrorMode(prev);
799 iov->m_Count = 0; // error nor not, all is lost
806 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
811 if ( bytes_written == 0 )
812 bytes_written = &tmp_int;
814 if ( m_Handle == INVALID_HANDLE_VALUE )
815 return Kumu::RESULT_STATE;
817 // suppress popup window on error
818 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
819 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
820 ::SetErrorMode(prev);
822 if ( result == 0 || *bytes_written != buf_len )
823 return Kumu::RESULT_WRITEFAIL;
825 return Kumu::RESULT_OK;
829 //------------------------------------------------------------------------------------------
834 Kumu::FileReader::OpenRead(const char* filename) const
836 KM_TEST_NULL_STR_L(filename);
837 const_cast<FileReader*>(this)->m_Filename = filename;
838 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
839 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
844 Kumu::FileReader::Close() const
846 if ( m_Handle == -1L )
847 return RESULT_FILEOPEN;
850 const_cast<FileReader*>(this)->m_Handle = -1L;
856 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
858 if ( m_Handle == -1L )
859 return RESULT_FILEOPEN;
861 if ( lseek(m_Handle, position, whence) == -1L )
862 return RESULT_BADSEEK;
869 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
873 if ( m_Handle == -1L )
874 return RESULT_FILEOPEN;
876 Kumu::fpos_t tmp_pos;
878 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
879 return RESULT_READFAIL;
887 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
893 if ( read_count == 0 )
894 read_count = &tmp_int;
898 if ( m_Handle == -1L )
899 return RESULT_FILEOPEN;
901 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
902 return RESULT_READFAIL;
904 *read_count = tmp_count;
905 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
909 //------------------------------------------------------------------------------------------
914 Kumu::FileWriter::OpenWrite(const char* filename)
916 KM_TEST_NULL_STR_L(filename);
917 m_Filename = filename;
918 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
920 if ( m_Handle == -1L )
922 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
923 return RESULT_FILEOPEN;
926 m_IOVec = new h__iovec;
932 Kumu::FileWriter::OpenModify(const char* filename)
934 KM_TEST_NULL_STR_L(filename);
935 m_Filename = filename;
936 m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
938 if ( m_Handle == -1L )
940 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
941 return RESULT_FILEOPEN;
944 m_IOVec = new h__iovec;
950 Kumu::FileWriter::Writev(ui32_t* bytes_written)
952 assert( ! m_IOVec.empty() );
953 register h__iovec* iov = m_IOVec;
956 if ( bytes_written == 0 )
957 bytes_written = &tmp_int;
959 if ( m_Handle == -1L )
963 for ( int i = 0; i < iov->m_Count; i++ )
964 total_size += iov->m_iovec[i].iov_len;
966 int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
968 if ( write_size == -1L || write_size != total_size )
969 return RESULT_WRITEFAIL;
972 *bytes_written = write_size;
978 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
983 if ( bytes_written == 0 )
984 bytes_written = &tmp_int;
986 if ( m_Handle == -1L )
989 int write_size = write(m_Handle, buf, buf_len);
991 if ( write_size == -1L || (ui32_t)write_size != buf_len )
992 return RESULT_WRITEFAIL;
994 *bytes_written = write_size;
1001 //------------------------------------------------------------------------------------------
1006 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1009 ui32_t read_size = 0;
1013 KM_TEST_NULL_STR_L(filename);
1015 Result_t result = File.OpenRead(filename);
1017 if ( KM_SUCCESS(result) )
1019 fsize = File.Size();
1021 if ( fsize > (Kumu::fpos_t)max_size )
1023 DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1024 return RESULT_ALLOC;
1029 DefaultLogSink().Error("%s: zero file size\n", filename);
1030 return RESULT_READFAIL;
1033 result = ReadBuf.Capacity((ui32_t)fsize);
1036 if ( KM_SUCCESS(result) )
1037 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1039 if ( KM_SUCCESS(result) )
1040 outString.assign((const char*)ReadBuf.RoData(), read_size);
1048 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1051 ui32_t write_count = 0;
1052 KM_TEST_NULL_STR_L(filename);
1054 Result_t result = File.OpenWrite(filename);
1056 if ( KM_SUCCESS(result) )
1057 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1063 //------------------------------------------------------------------------------------------
1066 // Win32 directory scanner
1073 Kumu::DirScanner::Open(const char* filename)
1075 KM_TEST_NULL_STR_L(filename);
1077 // we need to append a '*' to read the entire directory
1078 ui32_t fn_len = strlen(filename);
1079 char* tmp_file = (char*)malloc(fn_len + 8);
1081 if ( tmp_file == 0 )
1082 return RESULT_ALLOC;
1084 strcpy(tmp_file, filename);
1085 char* p = &tmp_file[fn_len] - 1;
1087 if ( *p != '/' && *p != '\\' )
1097 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1098 Result_t result = RESULT_OK;
1100 if ( m_Handle == -1 )
1101 result = RESULT_NOT_FOUND;
1110 Kumu::DirScanner::Close()
1112 if ( m_Handle == -1 )
1113 return RESULT_FILEOPEN;
1115 if ( _findclose((long)m_Handle) == -1 )
1123 // This sets filename param to the same per-instance buffer every time, so
1124 // the value will change on the next call
1126 Kumu::DirScanner::GetNext(char* filename)
1128 KM_TEST_NULL_L(filename);
1130 if ( m_Handle == -1 )
1131 return RESULT_FILEOPEN;
1133 if ( m_FileInfo.name[0] == '\0' )
1134 return RESULT_ENDOFFILE;
1136 strncpy(filename, m_FileInfo.name, MaxFilePath);
1137 Result_t result = RESULT_OK;
1139 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1141 m_FileInfo.name[0] = '\0';
1143 if ( errno != ENOENT )
1144 result = RESULT_FAIL;
1153 // POSIX directory scanner
1157 Kumu::DirScanner::Open(const char* filename)
1159 KM_TEST_NULL_STR_L(filename);
1161 Result_t result = RESULT_OK;
1163 if ( ( m_Handle = opendir(filename) ) == NULL )
1165 if ( errno == ENOENT )
1166 result = RESULT_ENDOFFILE;
1169 result = RESULT_FAIL;
1178 Kumu::DirScanner::Close()
1180 if ( m_Handle == NULL )
1181 return RESULT_FILEOPEN;
1183 if ( closedir(m_Handle) == -1 )
1193 Kumu::DirScanner::GetNext(char* filename)
1195 KM_TEST_NULL_L(filename);
1197 if ( m_Handle == NULL )
1198 return RESULT_FILEOPEN;
1200 struct dirent* entry;
1204 if ( ( entry = readdir(m_Handle)) == NULL )
1205 return RESULT_ENDOFFILE;
1210 strncpy(filename, entry->d_name, MaxFilePath);
1220 // end KM_fileio.cpp