2 Copyright (c) 2004-2006, 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 // AFAIK, there is no iovec equivalent in the win32 API
46 char* iov_base; // stupid iovec uses char*
51 typedef struct stat fstat_t;
56 do_stat(const char* path, fstat_t* stat_info)
58 KM_TEST_NULL_STR(path);
59 KM_TEST_NULL(stat_info);
61 Kumu::Result_t result = Kumu::RESULT_OK;
64 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
66 if ( _stati64(path, stat_info) == (__int64)-1 )
67 result = Kumu::RESULT_FILEOPEN;
69 ::SetErrorMode( prev );
71 if ( stat(path, stat_info) == -1L )
72 result = Kumu::RESULT_FILEOPEN;
74 if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
75 result = Kumu::RESULT_FILEOPEN;
85 do_fstat(HANDLE handle, fstat_t* stat_info)
87 KM_TEST_NULL(stat_info);
89 Kumu::Result_t result = Kumu::RESULT_OK;
91 if ( fstat(handle, stat_info) == -1L )
92 result = Kumu::RESULT_FILEOPEN;
94 if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
95 result = Kumu::RESULT_FILEOPEN;
105 Kumu::PathIsFile(const char* pathname)
110 if ( KM_SUCCESS(do_stat(pathname, &info)) )
112 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
122 Kumu::PathIsDirectory(const char* pathname)
127 if ( KM_SUCCESS(do_stat(pathname, &info)) )
129 if ( info.st_mode & S_IFDIR )
139 Kumu::FileSize(const char* pathname)
144 if ( KM_SUCCESS(do_stat(pathname, &info)) )
146 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
147 return(info.st_size);
153 //------------------------------------------------------------------------------------------
154 // portable aspects of the file classes
156 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
159 class Kumu::FileWriter::h__iovec
163 struct iovec m_iovec[IOVecMaxEntries];
164 h__iovec() : m_Count(0) {}
171 Kumu::FileReader::Size() const
174 return FileSize(m_Filename.c_str());
178 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
180 if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
181 return(info.st_size);
188 // these are declared here instead of in the header file
189 // because we have a mem_ptr that is managing a hidden class
190 Kumu::FileWriter::FileWriter() {}
191 Kumu::FileWriter::~FileWriter() {}
195 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
197 assert( ! m_IOVec.empty() );
198 register h__iovec* iov = m_IOVec;
201 if ( iov->m_Count >= IOVecMaxEntries )
203 DefaultLogSink().Error("The iovec is full! Only %lu entries allowed before a flush.\n",
208 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
209 iov->m_iovec[iov->m_Count].iov_len = buf_len;
217 //------------------------------------------------------------------------------------------
221 Kumu::FileReader::OpenRead(const char* filename) const
223 KM_TEST_NULL_STR(filename);
224 const_cast<FileReader*>(this)->m_Filename = filename;
226 // suppress popup window on error
227 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
229 const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
230 (GENERIC_READ), // open for reading
231 FILE_SHARE_READ, // share for reading
233 OPEN_EXISTING, // read
234 FILE_ATTRIBUTE_NORMAL, // normal file
235 NULL // no template file
238 ::SetErrorMode(prev);
240 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
241 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
246 Kumu::FileReader::Close() const
248 if ( m_Handle == INVALID_HANDLE_VALUE )
249 return Kumu::RESULT_FILEOPEN;
251 // suppress popup window on error
252 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
253 BOOL result = ::CloseHandle(m_Handle);
254 ::SetErrorMode(prev);
255 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
257 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
262 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
264 if ( m_Handle == INVALID_HANDLE_VALUE )
265 return Kumu::RESULT_STATE;
268 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
269 in.QuadPart = position;
270 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
271 HRESULT LastError = GetLastError();
272 ::SetErrorMode(prev);
274 if ( (LastError != NO_ERROR
275 && (in.LowPart == INVALID_SET_FILE_POINTER
276 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
277 return Kumu::RESULT_READFAIL;
279 return Kumu::RESULT_OK;
284 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
288 if ( m_Handle == (HANDLE)-1L )
289 return Kumu::RESULT_FILEOPEN;
292 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
293 in.QuadPart = (__int64)0;
294 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
295 HRESULT LastError = GetLastError();
296 ::SetErrorMode(prev);
298 if ( (LastError != NO_ERROR
299 && (in.LowPart == INVALID_SET_FILE_POINTER
300 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
301 return Kumu::RESULT_READFAIL;
303 *pos = (Kumu::fpos_t)in.QuadPart;
304 return Kumu::RESULT_OK;
309 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
312 Result_t result = Kumu::RESULT_OK;
316 if ( read_count == 0 )
317 read_count = &tmp_int;
321 if ( m_Handle == INVALID_HANDLE_VALUE )
322 return Kumu::RESULT_FILEOPEN;
324 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
325 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
326 result = Kumu::RESULT_READFAIL;
328 ::SetErrorMode(prev);
330 if ( tmp_count == 0 ) /* EOF */
331 result = Kumu::RESULT_ENDOFFILE;
333 if ( KM_SUCCESS(result) )
334 *read_count = tmp_count;
341 //------------------------------------------------------------------------------------------
346 Kumu::FileWriter::OpenWrite(const char* filename)
348 KM_TEST_NULL_STR(filename);
349 m_Filename = filename;
351 // suppress popup window on error
352 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
354 m_Handle = ::CreateFile(filename,
355 (GENERIC_WRITE|GENERIC_READ), // open for reading
356 FILE_SHARE_READ, // share for reading
358 CREATE_ALWAYS, // overwrite (beware!)
359 FILE_ATTRIBUTE_NORMAL, // normal file
360 NULL // no template file
363 ::SetErrorMode(prev);
365 if ( m_Handle == INVALID_HANDLE_VALUE )
366 return Kumu::RESULT_FILEOPEN;
368 m_IOVec = new h__iovec;
369 return Kumu::RESULT_OK;
374 Kumu::FileWriter::Writev(ui32_t* bytes_written)
376 assert( ! m_IOVec.empty() );
377 register h__iovec* iov = m_IOVec;
380 if ( bytes_written == 0 )
381 bytes_written = &tmp_int;
383 if ( m_Handle == INVALID_HANDLE_VALUE )
384 return Kumu::RESULT_STATE;
387 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
388 Result_t result = Kumu::RESULT_OK;
390 // AFAIK, there is no writev() equivalent in the win32 API
391 for ( register int i = 0; i < iov->m_Count; i++ )
393 ui32_t tmp_count = 0;
394 BOOL wr_result = ::WriteFile(m_Handle,
395 iov->m_iovec[i].iov_base,
396 iov->m_iovec[i].iov_len,
400 if ( wr_result == 0 )
402 result = Kumu::RESULT_WRITEFAIL;
406 assert(iov->m_iovec[i].iov_len == tmp_count);
407 *bytes_written += tmp_count;
410 ::SetErrorMode(prev);
411 iov->m_Count = 0; // error nor not, all is lost
418 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
423 if ( bytes_written == 0 )
424 bytes_written = &tmp_int;
426 if ( m_Handle == INVALID_HANDLE_VALUE )
427 return Kumu::RESULT_STATE;
429 // suppress popup window on error
430 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
431 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
432 ::SetErrorMode(prev);
434 return ( result == 0 ) ? Kumu::RESULT_WRITEFAIL : Kumu::RESULT_OK;
438 //------------------------------------------------------------------------------------------
443 Kumu::FileReader::OpenRead(const char* filename) const
445 KM_TEST_NULL_STR(filename);
446 const_cast<FileReader*>(this)->m_Filename = filename;
447 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
448 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
453 Kumu::FileReader::Close() const
455 if ( m_Handle == -1L )
456 return RESULT_FILEOPEN;
459 const_cast<FileReader*>(this)->m_Handle = -1L;
465 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
467 if ( m_Handle == -1L )
468 return RESULT_FILEOPEN;
470 if ( lseek(m_Handle, position, whence) == -1L )
471 return RESULT_BADSEEK;
478 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
482 if ( m_Handle == -1L )
483 return RESULT_FILEOPEN;
485 Kumu::fpos_t tmp_pos;
487 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
488 return RESULT_READFAIL;
496 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
502 if ( read_count == 0 )
503 read_count = &tmp_int;
507 if ( m_Handle == -1L )
508 return RESULT_FILEOPEN;
510 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
511 return RESULT_READFAIL;
513 *read_count = tmp_count;
514 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
518 //------------------------------------------------------------------------------------------
523 Kumu::FileWriter::OpenWrite(const char* filename)
525 KM_TEST_NULL_STR(filename);
526 m_Filename = filename;
527 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
529 if ( m_Handle == -1L )
531 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
532 return RESULT_FILEOPEN;
535 m_IOVec = new h__iovec;
541 Kumu::FileWriter::OpenModify(const char* filename)
543 KM_TEST_NULL_STR(filename);
544 m_Filename = filename;
545 m_Handle = open(filename, O_RDWR|O_CREAT, 0644);
547 if ( m_Handle == -1L )
549 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
550 return RESULT_FILEOPEN;
553 m_IOVec = new h__iovec;
559 Kumu::FileWriter::Writev(ui32_t* bytes_written)
561 assert( ! m_IOVec.empty() );
562 register h__iovec* iov = m_IOVec;
565 if ( bytes_written == 0 )
566 bytes_written = &tmp_int;
568 if ( m_Handle == -1L )
571 int read_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
573 if ( read_size == -1L )
574 return RESULT_WRITEFAIL;
577 *bytes_written = read_size;
583 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
588 if ( bytes_written == 0 )
589 bytes_written = &tmp_int;
594 if ( m_Handle == -1L )
597 int read_size = write(m_Handle, buf, buf_len);
599 if ( read_size == -1L )
600 return RESULT_WRITEFAIL;
602 *bytes_written = read_size;
609 //------------------------------------------------------------------------------------------
614 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
617 ui32_t read_size = 0;
621 KM_TEST_NULL_STR(filename);
623 Result_t result = File.OpenRead(filename);
625 if ( KM_SUCCESS(result) )
629 if ( fsize > (Kumu::fpos_t)max_size )
632 result = ReadBuf.Capacity((ui32_t)fsize);
635 if ( KM_SUCCESS(result) )
636 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
638 if ( KM_SUCCESS(result) )
639 outString.assign((const char*)ReadBuf.RoData(), read_size);
647 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
650 ui32_t write_count = 0;
651 KM_TEST_NULL_STR(filename);
653 Result_t result = File.OpenWrite(filename);
655 if ( KM_SUCCESS(result) )
656 result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
658 if ( KM_SUCCESS(result) && write_count != inString.length() )
659 return RESULT_WRITEFAIL;
665 //------------------------------------------------------------------------------------------
668 // Win32 directory scanner
675 Kumu::DirScanner::Open(const char* filename)
677 KM_TEST_NULL_STR(filename);
679 // we need to append a '*' to read the entire directory
680 ui32_t fn_len = strlen(filename);
681 char* tmp_file = (char*)malloc(fn_len + 8);
686 strcpy(tmp_file, filename);
687 char* p = &tmp_file[fn_len] - 1;
689 if ( *p != '/' && *p != '\\' )
699 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
700 Result_t result = RESULT_OK;
702 if ( m_Handle == -1 )
703 result = RESULT_NOT_FOUND;
712 Kumu::DirScanner::Close()
714 if ( m_Handle == -1 )
715 return RESULT_FILEOPEN;
717 if ( _findclose((long)m_Handle) == -1 )
725 // This sets filename param to the same per-instance buffer every time, so
726 // the value will change on the next call
728 Kumu::DirScanner::GetNext(char* filename)
730 KM_TEST_NULL(filename);
732 if ( m_Handle == -1 )
733 return RESULT_FILEOPEN;
735 if ( m_FileInfo.name[0] == '\0' )
736 return RESULT_ENDOFFILE;
738 strncpy(filename, m_FileInfo.name, MaxFilePath);
739 Result_t result = RESULT_OK;
741 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
743 m_FileInfo.name[0] = '\0';
745 if ( errno != ENOENT )
746 result = RESULT_FAIL;
755 // POSIX directory scanner
759 Kumu::DirScanner::Open(const char* filename)
761 KM_TEST_NULL_STR(filename);
763 Result_t result = RESULT_OK;
765 if ( ( m_Handle = opendir(filename) ) == NULL )
767 if ( errno == ENOENT )
768 result = RESULT_ENDOFFILE;
771 result = RESULT_FAIL;
780 Kumu::DirScanner::Close()
782 if ( m_Handle == NULL )
783 return RESULT_FILEOPEN;
785 if ( closedir(m_Handle) == -1 )
795 Kumu::DirScanner::GetNext(char* filename)
797 KM_TEST_NULL(filename);
799 if ( m_Handle == NULL )
800 return RESULT_FILEOPEN;
802 struct dirent* entry;
806 if ( ( entry = readdir(m_Handle)) == NULL )
807 return RESULT_ENDOFFILE;
812 strncpy(filename, entry->d_name, MaxFilePath);