PathIs* were too noisy
[asdcplib.git] / src / KM_fileio.cpp
1 /*
2 Copyright (c) 2004-2006, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
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.
15
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.
26 */
27   /*! \file    KM_fileio.cpp
28     \version $Id$
29     \brief   portable file i/o
30   */
31
32 #include <KM_fileio.h>
33 #include <KM_log.h>
34 #include <fcntl.h>
35 #include <assert.h>
36
37 using namespace Kumu;
38
39 #ifdef KM_WIN32
40 typedef struct _stati64 fstat_t;
41 #define S_IFLNK 0
42
43
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.
47 struct iovec {
48   char* iov_base; // stupid iovec uses char*
49   int   iov_len;
50 };
51 #else
52 #include <sys/uio.h>
53 typedef struct stat     fstat_t;
54 #endif
55
56 //
57 static Kumu::Result_t
58 do_stat(const char* path, fstat_t* stat_info)
59 {
60   KM_TEST_NULL_STR_L(path);
61   KM_TEST_NULL_L(stat_info);
62
63   Kumu::Result_t result = Kumu::RESULT_OK;
64
65 #ifdef KM_WIN32
66   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
67
68   if ( _stati64(path, stat_info) == (__int64)-1 )
69     result = Kumu::RESULT_FILEOPEN;
70
71   ::SetErrorMode( prev );
72 #else
73   if ( stat(path, stat_info) == -1L )
74     result = Kumu::RESULT_FILEOPEN;
75
76   if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
77     result = Kumu::RESULT_FILEOPEN;
78 #endif
79
80   return result;
81 }
82
83 #ifndef KM_WIN32
84
85 //
86 static Kumu::Result_t
87 do_fstat(HANDLE handle, fstat_t* stat_info)
88 {
89   KM_TEST_NULL_L(stat_info);
90
91   Kumu::Result_t result = Kumu::RESULT_OK;
92
93   if ( fstat(handle, stat_info) == -1L )
94     result = Kumu::RESULT_FILEOPEN;
95
96   if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
97     result = Kumu::RESULT_FILEOPEN;
98
99   return result;
100 }
101
102 #endif
103
104
105 //
106 bool
107 Kumu::PathIsFile(const char* pathname)
108 {
109   if ( pathname == 0 || *pathname == 0 )
110     return false;
111
112   fstat_t info;
113
114   if ( KM_SUCCESS(do_stat(pathname, &info)) )
115     {
116       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
117         return true;
118     }
119
120   return false;
121 }
122
123
124 //
125 bool
126 Kumu::PathIsDirectory(const char* pathname)
127 {
128   if ( pathname == 0 || *pathname == 0 )
129     return false;
130
131   fstat_t info;
132
133   if ( KM_SUCCESS(do_stat(pathname, &info)) )
134     {
135       if ( info.st_mode & S_IFDIR )
136         return true;
137     }
138
139   return false;
140 }
141
142
143 //
144 Kumu::fsize_t
145 Kumu::FileSize(const char* pathname)
146 {
147   if ( pathname == 0 || *pathname == 0 )
148     return false;
149
150   fstat_t info;
151
152   if ( KM_SUCCESS(do_stat(pathname, &info)) )
153     {
154       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
155         return(info.st_size);
156     }
157
158   return 0;
159 }
160
161 //------------------------------------------------------------------------------------------
162 // portable aspects of the file classes
163
164 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
165
166 //
167 class Kumu::FileWriter::h__iovec
168 {
169 public:
170   int            m_Count;
171   struct iovec   m_iovec[IOVecMaxEntries];
172   h__iovec() : m_Count(0) {}
173 };
174
175
176
177 //
178 Kumu::fsize_t
179 Kumu::FileReader::Size() const
180 {
181 #ifdef KM_WIN32
182   return FileSize(m_Filename.c_str());
183 #else
184   fstat_t info;
185
186   if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
187     {
188       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
189         return(info.st_size);
190     }
191 #endif
192
193   return 0;
194 }
195
196 // these are declared here instead of in the header file
197 // because we have a mem_ptr that is managing a hidden class
198 Kumu::FileWriter::FileWriter() {}
199 Kumu::FileWriter::~FileWriter() {}
200
201 //
202 Kumu::Result_t
203 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
204 {
205   assert( ! m_IOVec.empty() );
206   register h__iovec* iov = m_IOVec;
207   KM_TEST_NULL_L(buf);
208
209   if ( iov->m_Count >= IOVecMaxEntries )
210     {
211       DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
212                              IOVecMaxEntries);
213       return RESULT_FAIL;
214     }
215
216   iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
217   iov->m_iovec[iov->m_Count].iov_len = buf_len;
218   iov->m_Count++;
219
220   return RESULT_OK;
221 }
222
223
224 #ifdef KM_WIN32
225 //------------------------------------------------------------------------------------------
226 //
227
228 Kumu::Result_t
229 Kumu::FileReader::OpenRead(const char* filename) const
230 {
231   KM_TEST_NULL_STR_L(filename);
232   const_cast<FileReader*>(this)->m_Filename = filename;
233   
234   // suppress popup window on error
235   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
236
237   const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
238                           (GENERIC_READ),                // open for reading
239                           FILE_SHARE_READ,               // share for reading
240                           NULL,                          // no security
241                           OPEN_EXISTING,                 // read
242                           FILE_ATTRIBUTE_NORMAL,         // normal file
243                           NULL                           // no template file
244                           );
245
246   ::SetErrorMode(prev);
247
248   return ( m_Handle == INVALID_HANDLE_VALUE ) ?
249     Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
250 }
251
252 //
253 Kumu::Result_t
254 Kumu::FileReader::Close() const
255 {
256   if ( m_Handle == INVALID_HANDLE_VALUE )
257     return Kumu::RESULT_FILEOPEN;
258
259   // suppress popup window on error
260   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
261   BOOL result = ::CloseHandle(m_Handle);
262   ::SetErrorMode(prev);
263   const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
264
265   return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
266 }
267
268 //
269 Kumu::Result_t
270 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
271 {
272   if ( m_Handle == INVALID_HANDLE_VALUE )
273     return Kumu::RESULT_STATE;
274
275   LARGE_INTEGER in;
276   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
277   in.QuadPart = position;
278   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
279   HRESULT LastError = GetLastError();
280   ::SetErrorMode(prev);
281
282   if ( (LastError != NO_ERROR
283         && (in.LowPart == INVALID_SET_FILE_POINTER
284             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
285     return Kumu::RESULT_READFAIL;
286   
287   return Kumu::RESULT_OK;
288 }
289
290 //
291 Kumu::Result_t
292 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
293 {
294   KM_TEST_NULL_L(pos);
295
296   if ( m_Handle == (HANDLE)-1L )
297     return Kumu::RESULT_FILEOPEN;
298
299   LARGE_INTEGER in;
300   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
301   in.QuadPart = (__int64)0;
302   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
303   HRESULT LastError = GetLastError();
304   ::SetErrorMode(prev);
305
306   if ( (LastError != NO_ERROR
307         && (in.LowPart == INVALID_SET_FILE_POINTER
308             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
309     return Kumu::RESULT_READFAIL;
310
311   *pos = (Kumu::fpos_t)in.QuadPart;
312   return Kumu::RESULT_OK;
313 }
314
315 //
316 Kumu::Result_t
317 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
318 {
319   KM_TEST_NULL_L(buf);
320   Result_t result = Kumu::RESULT_OK;
321   DWORD    tmp_count;
322   ui32_t tmp_int;
323
324   if ( read_count == 0 )
325     read_count = &tmp_int;
326
327   *read_count = 0;
328
329   if ( m_Handle == INVALID_HANDLE_VALUE )
330     return Kumu::RESULT_FILEOPEN;
331   
332   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
333   if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
334     result = Kumu::RESULT_READFAIL;
335
336   ::SetErrorMode(prev);
337
338   if ( tmp_count == 0 ) /* EOF */
339     result = Kumu::RESULT_ENDOFFILE;
340
341   if ( KM_SUCCESS(result) )
342     *read_count = tmp_count;
343
344   return result;
345 }
346
347
348
349 //------------------------------------------------------------------------------------------
350 //
351
352 //
353 Kumu::Result_t
354 Kumu::FileWriter::OpenWrite(const char* filename)
355 {
356   KM_TEST_NULL_STR_L(filename);
357   m_Filename = filename;
358   
359   // suppress popup window on error
360   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
361
362   m_Handle = ::CreateFile(filename,
363                           (GENERIC_WRITE|GENERIC_READ),  // open for reading
364                           FILE_SHARE_READ,               // share for reading
365                           NULL,                          // no security
366                           CREATE_ALWAYS,                 // overwrite (beware!)
367                           FILE_ATTRIBUTE_NORMAL,         // normal file
368                           NULL                           // no template file
369                           );
370
371   ::SetErrorMode(prev);
372
373   if ( m_Handle == INVALID_HANDLE_VALUE )
374     return Kumu::RESULT_FILEOPEN;
375   
376   m_IOVec = new h__iovec;
377   return Kumu::RESULT_OK;
378 }
379
380 //
381 Kumu::Result_t
382 Kumu::FileWriter::Writev(ui32_t* bytes_written)
383 {
384   assert( ! m_IOVec.empty() );
385   register h__iovec* iov = m_IOVec;
386   ui32_t tmp_int;
387
388   if ( bytes_written == 0 )
389     bytes_written = &tmp_int;
390
391   if ( m_Handle == INVALID_HANDLE_VALUE )
392     return Kumu::RESULT_STATE;
393
394   *bytes_written = 0;
395   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
396   Result_t result = Kumu::RESULT_OK;
397
398   // AFAIK, there is no writev() equivalent in the win32 API
399   for ( register int i = 0; i < iov->m_Count; i++ )
400     {
401       ui32_t tmp_count = 0;
402       BOOL wr_result = ::WriteFile(m_Handle,
403                                    iov->m_iovec[i].iov_base,
404                                    iov->m_iovec[i].iov_len,
405                                    (DWORD*)&tmp_count,
406                                    NULL);
407
408       if ( wr_result == 0 )
409         {
410           result = Kumu::RESULT_WRITEFAIL;
411           break;
412         }
413
414       assert(iov->m_iovec[i].iov_len == tmp_count);
415       *bytes_written += tmp_count;
416     }
417
418   ::SetErrorMode(prev);
419   iov->m_Count = 0; // error nor not, all is lost
420
421   return result;
422 }
423
424 //
425 Kumu::Result_t
426 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
427 {
428   KM_TEST_NULL_L(buf);
429   ui32_t tmp_int;
430
431   if ( bytes_written == 0 )
432     bytes_written = &tmp_int;
433
434   if ( m_Handle == INVALID_HANDLE_VALUE )
435     return Kumu::RESULT_STATE;
436
437   // suppress popup window on error
438   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
439   BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
440   ::SetErrorMode(prev);
441
442   return ( result == 0 ) ? Kumu::RESULT_WRITEFAIL : Kumu::RESULT_OK;
443 }
444
445 #else // KM_WIN32
446 //------------------------------------------------------------------------------------------
447 // POSIX
448
449 //
450 Kumu::Result_t
451 Kumu::FileReader::OpenRead(const char* filename) const
452 {
453   KM_TEST_NULL_STR_L(filename);
454   const_cast<FileReader*>(this)->m_Filename = filename;
455   const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
456   return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
457 }
458
459 //
460 Kumu::Result_t
461 Kumu::FileReader::Close() const
462 {
463   if ( m_Handle == -1L )
464     return RESULT_FILEOPEN;
465
466   close(m_Handle);
467   const_cast<FileReader*>(this)->m_Handle = -1L;
468   return RESULT_OK;
469 }
470
471 //
472 Kumu::Result_t
473 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
474 {
475   if ( m_Handle == -1L )
476     return RESULT_FILEOPEN;
477
478   if ( lseek(m_Handle, position, whence) == -1L )
479     return RESULT_BADSEEK;
480
481   return RESULT_OK;
482 }
483
484 //
485 Kumu::Result_t
486 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
487 {
488   KM_TEST_NULL_L(pos);
489
490   if ( m_Handle == -1L )
491     return RESULT_FILEOPEN;
492
493   Kumu::fpos_t tmp_pos;
494
495   if (  (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
496     return RESULT_READFAIL;
497
498   *pos = tmp_pos;
499   return RESULT_OK;
500 }
501
502 //
503 Kumu::Result_t
504 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
505 {
506   KM_TEST_NULL_L(buf);
507   i32_t  tmp_count = 0;
508   ui32_t tmp_int = 0;
509
510   if ( read_count == 0 )
511     read_count = &tmp_int;
512
513   *read_count = 0;
514
515   if ( m_Handle == -1L )
516     return RESULT_FILEOPEN;
517
518   if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
519     return RESULT_READFAIL;
520
521   *read_count = tmp_count;
522   return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
523 }
524
525
526 //------------------------------------------------------------------------------------------
527 //
528
529 //
530 Kumu::Result_t
531 Kumu::FileWriter::OpenWrite(const char* filename)
532 {
533   KM_TEST_NULL_STR_L(filename);
534   m_Filename = filename;
535   m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
536
537   if ( m_Handle == -1L )
538     {
539       DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
540       return RESULT_FILEOPEN;
541     }
542
543   m_IOVec = new h__iovec;
544   return RESULT_OK;
545 }
546
547 //
548 Kumu::Result_t
549 Kumu::FileWriter::OpenModify(const char* filename)
550 {
551   KM_TEST_NULL_STR_L(filename);
552   m_Filename = filename;
553   m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
554
555   if ( m_Handle == -1L )
556     {
557       DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
558       return RESULT_FILEOPEN;
559     }
560
561   m_IOVec = new h__iovec;
562   return RESULT_OK;
563 }
564
565 //
566 Kumu::Result_t
567 Kumu::FileWriter::Writev(ui32_t* bytes_written)
568 {
569   assert( ! m_IOVec.empty() );
570   register h__iovec* iov = m_IOVec;
571   ui32_t tmp_int;
572
573   if ( bytes_written == 0 )
574     bytes_written = &tmp_int;
575
576   if ( m_Handle == -1L )
577     return RESULT_STATE;
578
579   int read_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
580   
581   if ( read_size == -1L )
582     return RESULT_WRITEFAIL;
583
584   iov->m_Count = 0;
585   *bytes_written = read_size;  
586   return RESULT_OK;
587 }
588
589 //
590 Kumu::Result_t
591 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
592 {
593   KM_TEST_NULL_L(buf);
594   ui32_t tmp_int;
595
596   if ( bytes_written == 0 )
597     bytes_written = &tmp_int;
598
599   // TODO: flush iovec
600
601
602   if ( m_Handle == -1L )
603     return RESULT_STATE;
604
605   int read_size = write(m_Handle, buf, buf_len);
606   
607   if ( read_size == -1L )
608     return RESULT_WRITEFAIL;
609
610   *bytes_written = read_size;  
611   return RESULT_OK;
612 }
613
614
615 #endif // KM_WIN32
616
617 //------------------------------------------------------------------------------------------
618
619
620 //
621 Kumu::Result_t
622 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
623 {
624   fsize_t    fsize = 0;
625   ui32_t     read_size = 0;
626   FileReader File;
627   ByteString ReadBuf;
628
629   KM_TEST_NULL_STR_L(filename);
630
631   Result_t result = File.OpenRead(filename);
632
633   if ( KM_SUCCESS(result) )
634     {
635       fsize = File.Size();
636
637       if ( fsize > (Kumu::fpos_t)max_size )
638         {
639           DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
640           return RESULT_ALLOC;
641         }
642
643       if ( fsize == 0 )
644         {
645           DefaultLogSink().Error("%s: zero file size\n", filename);
646           return RESULT_ALLOC;
647         }
648
649       result = ReadBuf.Capacity((ui32_t)fsize);
650     }
651
652   if ( KM_SUCCESS(result) )
653     result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
654
655   if ( KM_SUCCESS(result) )
656     outString.assign((const char*)ReadBuf.RoData(), read_size);
657
658   return result;
659 }
660
661
662 //
663 Kumu::Result_t
664 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
665 {
666   FileWriter File;
667   ui32_t write_count = 0;
668   KM_TEST_NULL_STR_L(filename);
669
670   Result_t result = File.OpenWrite(filename);
671
672   if ( KM_SUCCESS(result) )
673     result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
674
675   if ( KM_SUCCESS(result) && write_count != inString.length() )
676     return RESULT_WRITEFAIL;
677
678   return RESULT_OK;
679 }
680
681
682 //------------------------------------------------------------------------------------------
683 //
684
685 // Win32 directory scanner
686 //
687 #ifdef KM_WIN32
688
689 //
690 //
691 Result_t
692 Kumu::DirScanner::Open(const char* filename)
693 {
694   KM_TEST_NULL_STR_L(filename);
695
696   // we need to append a '*' to read the entire directory
697   ui32_t fn_len = strlen(filename); 
698   char* tmp_file = (char*)malloc(fn_len + 8);
699
700   if ( tmp_file == 0 )
701     return RESULT_ALLOC;
702
703   strcpy(tmp_file, filename);
704   char* p = &tmp_file[fn_len] - 1;
705
706   if ( *p != '/' && *p != '\\' )
707     {
708       p++;
709       *p++ = '/';
710     }
711
712   *p++ = '*';
713   *p = 0;
714   // whew...
715
716   m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
717   Result_t result = RESULT_OK;
718
719   if ( m_Handle == -1 )
720     result = RESULT_NOT_FOUND;
721
722   return result;
723 }
724
725
726 //
727 //
728 Result_t
729 Kumu::DirScanner::Close()
730 {
731   if ( m_Handle == -1 )
732     return RESULT_FILEOPEN;
733
734   if ( _findclose((long)m_Handle) == -1 )
735     return RESULT_FAIL;
736
737   m_Handle = -1;
738   return RESULT_OK;
739 }
740
741
742 // This sets filename param to the same per-instance buffer every time, so
743 // the value will change on the next call
744 Result_t
745 Kumu::DirScanner::GetNext(char* filename)
746 {
747   KM_TEST_NULL_L(filename);
748
749   if ( m_Handle == -1 )
750     return RESULT_FILEOPEN;
751
752   if ( m_FileInfo.name[0] == '\0' )
753     return RESULT_ENDOFFILE;
754
755   strncpy(filename, m_FileInfo.name, MaxFilePath);
756   Result_t result = RESULT_OK;
757
758   if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
759     {
760       m_FileInfo.name[0] = '\0';
761           
762       if ( errno != ENOENT )
763         result = RESULT_FAIL;
764     }
765
766   return result;
767 }
768
769
770 #else // KM_WIN32
771
772 // POSIX directory scanner
773
774 //
775 Result_t
776 Kumu::DirScanner::Open(const char* filename)
777 {
778   KM_TEST_NULL_STR_L(filename);
779
780   Result_t result = RESULT_OK;
781
782   if ( ( m_Handle = opendir(filename) ) == NULL )
783     {
784       if ( errno == ENOENT )
785         result = RESULT_ENDOFFILE;
786
787       else
788         result = RESULT_FAIL;
789     }
790
791   return result;
792 }
793
794
795 //
796 Result_t
797 Kumu::DirScanner::Close()
798 {
799   if ( m_Handle == NULL )
800     return RESULT_FILEOPEN;
801
802   if ( closedir(m_Handle) == -1 )
803     return RESULT_FAIL;
804
805   m_Handle = NULL;
806   return RESULT_OK;
807 }
808
809
810 //
811 Result_t
812 Kumu::DirScanner::GetNext(char* filename)
813 {
814   KM_TEST_NULL_L(filename);
815
816   if ( m_Handle == NULL )
817     return RESULT_FILEOPEN;
818
819   struct dirent* entry;
820
821   for (;;)
822     {
823       if ( ( entry = readdir(m_Handle)) == NULL )
824         return RESULT_ENDOFFILE;
825
826       break;
827     }
828
829   strncpy(filename, entry->d_name, MaxFilePath);
830   return RESULT_OK;
831 }
832
833
834 #endif // KM_WIN32
835
836
837
838 //
839 // end KM_fileio.cpp
840 //