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;
42 // AFAIK, there is no iovec equivalent in the win32 API
44 char* iov_base; // stupid iovec uses char*
49 typedef struct stat fstat_t;
54 do_stat(const char* path, fstat_t* stat_info)
56 KM_TEST_NULL_STR(path);
57 KM_TEST_NULL(stat_info);
59 Kumu::Result_t result = Kumu::RESULT_OK;
62 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
64 if ( _stati64(path, stat_info) == (__int64)-1 )
65 result = Kumu::RESULT_FILEOPEN;
67 ::SetErrorMode( prev );
69 if ( stat(path, stat_info) == -1L )
70 result = Kumu::RESULT_FILEOPEN;
72 if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
73 result = Kumu::RESULT_FILEOPEN;
83 do_fstat(HANDLE handle, fstat_t* stat_info)
85 KM_TEST_NULL(stat_info);
87 Kumu::Result_t result = Kumu::RESULT_OK;
89 if ( fstat(handle, stat_info) == -1L )
90 result = Kumu::RESULT_FILEOPEN;
92 if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
93 result = Kumu::RESULT_FILEOPEN;
103 Kumu::PathIsFile(const char* pathname)
108 if ( KM_SUCCESS(do_stat(pathname, &info)) )
110 if ( info.st_mode & S_IFREG )
120 Kumu::PathIsDirectory(const char* pathname)
125 if ( KM_SUCCESS(do_stat(pathname, &info)) )
127 if ( info.st_mode & S_IFDIR )
137 Kumu::FileSize(const char* pathname)
142 if ( KM_SUCCESS(do_stat(pathname, &info)) )
144 if ( info.st_mode & S_IFREG )
145 return(info.st_size);
151 //------------------------------------------------------------------------------------------
152 // portable aspects of the file classes
154 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
157 class Kumu::FileWriter::h__iovec
161 struct iovec m_iovec[IOVecMaxEntries];
162 h__iovec() : m_Count(0) {}
169 Kumu::FileReader::Size() const
172 return FileSize(m_Filename.c_str());
176 if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
178 if ( info.st_mode & S_IFREG )
179 return(info.st_size);
186 // these are declared here instead of in the header file
187 // because we have a mem_ptr that is managing a hidden class
188 Kumu::FileWriter::FileWriter() {}
189 Kumu::FileWriter::~FileWriter() {}
193 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
195 assert( ! m_IOVec.empty() );
196 register h__iovec* iov = m_IOVec;
199 if ( iov->m_Count >= IOVecMaxEntries )
201 DefaultLogSink().Error("The iovec is full! Only %lu entries allowed before a flush.\n",
206 iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
207 iov->m_iovec[iov->m_Count].iov_len = buf_len;
215 //------------------------------------------------------------------------------------------
219 Kumu::FileReader::OpenRead(const char* filename) const
221 KM_TEST_NULL_STR(filename);
222 const_cast<FileReader*>(this)->m_Filename = filename;
224 // suppress popup window on error
225 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
227 const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
228 (GENERIC_READ), // open for reading
229 FILE_SHARE_READ, // share for reading
231 OPEN_EXISTING, // read
232 FILE_ATTRIBUTE_NORMAL, // normal file
233 NULL // no template file
236 ::SetErrorMode(prev);
238 return ( m_Handle == INVALID_HANDLE_VALUE ) ?
239 Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
244 Kumu::FileReader::Close() const
246 if ( m_Handle == INVALID_HANDLE_VALUE )
247 return Kumu::RESULT_FILEOPEN;
249 // suppress popup window on error
250 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
251 BOOL result = ::CloseHandle(m_Handle);
252 ::SetErrorMode(prev);
253 const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
255 return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
260 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
262 if ( m_Handle == INVALID_HANDLE_VALUE )
263 return Kumu::RESULT_STATE;
266 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
267 in.QuadPart = position;
268 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
269 HRESULT LastError = GetLastError();
270 ::SetErrorMode(prev);
272 if ( (LastError != NO_ERROR
273 && (in.LowPart == INVALID_SET_FILE_POINTER
274 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
275 return Kumu::RESULT_READFAIL;
277 return Kumu::RESULT_OK;
282 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
286 if ( m_Handle == (HANDLE)-1L )
287 return Kumu::RESULT_FILEOPEN;
290 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
291 in.QuadPart = (__int64)0;
292 in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
293 HRESULT LastError = GetLastError();
294 ::SetErrorMode(prev);
296 if ( (LastError != NO_ERROR
297 && (in.LowPart == INVALID_SET_FILE_POINTER
298 || in.LowPart == ERROR_NEGATIVE_SEEK )) )
299 return Kumu::RESULT_READFAIL;
301 *pos = (Kumu::fpos_t)in.QuadPart;
302 return Kumu::RESULT_OK;
307 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
310 Result_t result = Kumu::RESULT_OK;
314 if ( read_count == 0 )
315 read_count = &tmp_int;
319 if ( m_Handle == INVALID_HANDLE_VALUE )
320 return Kumu::RESULT_FILEOPEN;
322 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
323 if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
324 result = Kumu::RESULT_READFAIL;
326 ::SetErrorMode(prev);
328 if ( tmp_count == 0 ) /* EOF */
329 result = Kumu::RESULT_ENDOFFILE;
331 if ( KM_SUCCESS(result) )
332 *read_count = tmp_count;
339 //------------------------------------------------------------------------------------------
344 Kumu::FileWriter::OpenWrite(const char* filename)
346 KM_TEST_NULL_STR(filename);
347 m_Filename = filename;
349 // suppress popup window on error
350 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
352 m_Handle = ::CreateFile(filename,
353 (GENERIC_WRITE|GENERIC_READ), // open for reading
354 FILE_SHARE_READ, // share for reading
356 CREATE_ALWAYS, // overwrite (beware!)
357 FILE_ATTRIBUTE_NORMAL, // normal file
358 NULL // no template file
361 ::SetErrorMode(prev);
363 if ( m_Handle == INVALID_HANDLE_VALUE )
364 return Kumu::RESULT_FILEOPEN;
366 m_IOVec = new h__iovec;
367 return Kumu::RESULT_OK;
372 Kumu::FileWriter::Writev(ui32_t* bytes_written)
374 assert( ! m_IOVec.empty() );
375 register h__iovec* iov = m_IOVec;
378 if ( bytes_written == 0 )
379 bytes_written = &tmp_int;
381 if ( m_Handle == INVALID_HANDLE_VALUE )
382 return Kumu::RESULT_STATE;
385 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
386 Result_t result = Kumu::RESULT_OK;
388 // AFAIK, there is no writev() equivalent in the win32 API
389 for ( register int i = 0; i < iov->m_Count; i++ )
391 ui32_t tmp_count = 0;
392 BOOL wr_result = ::WriteFile(m_Handle,
393 iov->m_iovec[i].iov_base,
394 iov->m_iovec[i].iov_len,
398 if ( wr_result == 0 )
400 result = Kumu::RESULT_WRITEFAIL;
404 assert(iov->m_iovec[i].iov_len == tmp_count);
405 *bytes_written += tmp_count;
408 ::SetErrorMode(prev);
409 iov->m_Count = 0; // error nor not, all is lost
416 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
421 if ( bytes_written == 0 )
422 bytes_written = &tmp_int;
424 if ( m_Handle == INVALID_HANDLE_VALUE )
425 return Kumu::RESULT_STATE;
427 // suppress popup window on error
428 UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
429 BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
430 ::SetErrorMode(prev);
432 return ( result == 0 ) ? Kumu::RESULT_WRITEFAIL : Kumu::RESULT_OK;
436 //------------------------------------------------------------------------------------------
441 Kumu::FileReader::OpenRead(const char* filename) const
443 KM_TEST_NULL_STR(filename);
444 const_cast<FileReader*>(this)->m_Filename = filename;
445 const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
446 return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
451 Kumu::FileReader::Close() const
453 if ( m_Handle == -1L )
454 return RESULT_FILEOPEN;
457 const_cast<FileReader*>(this)->m_Handle = -1L;
463 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
465 if ( m_Handle == -1L )
466 return RESULT_FILEOPEN;
468 if ( lseek(m_Handle, position, whence) == -1L )
469 return RESULT_BADSEEK;
476 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
480 if ( m_Handle == -1L )
481 return RESULT_FILEOPEN;
483 Kumu::fpos_t tmp_pos;
485 if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
486 return RESULT_READFAIL;
494 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
500 if ( read_count == 0 )
501 read_count = &tmp_int;
505 if ( m_Handle == -1L )
506 return RESULT_FILEOPEN;
508 if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
509 return RESULT_READFAIL;
511 *read_count = tmp_count;
512 return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
516 //------------------------------------------------------------------------------------------
521 Kumu::FileWriter::OpenWrite(const char* filename)
523 KM_TEST_NULL_STR(filename);
524 m_Filename = filename;
525 m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
527 if ( m_Handle == -1L )
529 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
530 return RESULT_FILEOPEN;
533 m_IOVec = new h__iovec;
539 Kumu::FileWriter::OpenModify(const char* filename)
541 KM_TEST_NULL_STR(filename);
542 m_Filename = filename;
543 m_Handle = open(filename, O_RDWR|O_CREAT, 0644);
545 if ( m_Handle == -1L )
547 DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
548 return RESULT_FILEOPEN;
551 m_IOVec = new h__iovec;
557 Kumu::FileWriter::Writev(ui32_t* bytes_written)
559 assert( ! m_IOVec.empty() );
560 register h__iovec* iov = m_IOVec;
563 if ( bytes_written == 0 )
564 bytes_written = &tmp_int;
566 if ( m_Handle == -1L )
569 int read_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
571 if ( read_size == -1L )
572 return RESULT_WRITEFAIL;
575 *bytes_written = read_size;
581 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
586 if ( bytes_written == 0 )
587 bytes_written = &tmp_int;
592 if ( m_Handle == -1L )
595 int read_size = write(m_Handle, buf, buf_len);
597 if ( read_size == -1L )
598 return RESULT_WRITEFAIL;
600 *bytes_written = read_size;
607 //------------------------------------------------------------------------------------------
612 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
619 KM_TEST_NULL_STR(filename);
621 Result_t result = File.OpenRead(filename);
623 if ( KM_SUCCESS(result) )
627 if ( fsize > max_size )
630 result = ReadBuf.Capacity((ui32_t)fsize);
633 if ( KM_SUCCESS(result) )
634 result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
636 if ( KM_SUCCESS(result) )
637 outString.assign((const char*)ReadBuf.RoData(), read_size);
643 //------------------------------------------------------------------------------------------
646 // Win32 directory scanner
653 Kumu::DirScanner::Open(const char* filename)
655 KM_TEST_NULL_STR(filename);
657 // we need to append a '*' to read the entire directory
658 ui32_t fn_len = strlen(filename);
659 char* tmp_file = (char*)malloc(fn_len + 8);
664 strcpy(tmp_file, filename);
665 char* p = &tmp_file[fn_len] - 1;
667 if ( *p != '/' && *p != '\\' )
677 m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
678 Result_t result = RESULT_OK;
680 if ( m_Handle == -1 )
681 result = RESULT_NOT_FOUND;
690 Kumu::DirScanner::Close()
692 if ( m_Handle == -1 )
693 return RESULT_FILEOPEN;
695 if ( _findclose((long)m_Handle) == -1 )
703 // This sets filename param to the same per-instance buffer every time, so
704 // the value will change on the next call
706 Kumu::DirScanner::GetNext(char* filename)
708 KM_TEST_NULL(filename);
710 if ( m_Handle == -1 )
711 return RESULT_FILEOPEN;
713 if ( m_FileInfo.name[0] == '\0' )
714 return RESULT_ENDOFFILE;
716 strncpy(filename, m_FileInfo.name, MaxFilePath);
717 Result_t result = RESULT_OK;
719 if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
721 m_FileInfo.name[0] = '\0';
723 if ( errno != ENOENT )
724 result = RESULT_FAIL;
733 // POSIX directory scanner
737 Kumu::DirScanner::Open(const char* filename)
739 KM_TEST_NULL_STR(filename);
741 Result_t result = RESULT_OK;
743 if ( ( m_Handle = opendir(filename) ) == NULL )
745 if ( errno == ENOENT )
746 result = RESULT_ENDOFFILE;
749 result = RESULT_FAIL;
758 Kumu::DirScanner::Close()
760 if ( m_Handle == NULL )
761 return RESULT_FILEOPEN;
763 if ( closedir(m_Handle) == -1 )
773 Kumu::DirScanner::GetNext(char* filename)
775 KM_TEST_NULL(filename);
777 if ( m_Handle == NULL )
778 return RESULT_FILEOPEN;
780 struct dirent* entry;
784 if ( ( entry = readdir(m_Handle)) == NULL )
785 return RESULT_ENDOFFILE;
790 strncpy(filename, entry->d_name, MaxFilePath);