added identifier list type
[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_WRITEFAIL;
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 || tmp_count != iov->m_iovec[i].iov_len)
409         {
410           result = Kumu::RESULT_WRITEFAIL;
411           break;
412         }
413
414       *bytes_written += tmp_count;
415     }
416
417   ::SetErrorMode(prev);
418   iov->m_Count = 0; // error nor not, all is lost
419
420   return result;
421 }
422
423 //
424 Kumu::Result_t
425 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
426 {
427   KM_TEST_NULL_L(buf);
428   ui32_t tmp_int;
429
430   if ( bytes_written == 0 )
431     bytes_written = &tmp_int;
432
433   if ( m_Handle == INVALID_HANDLE_VALUE )
434     return Kumu::RESULT_STATE;
435
436   // suppress popup window on error
437   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
438   BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
439   ::SetErrorMode(prev);
440
441   if ( result == 0 || bytes_written != buf_len )
442     return Kumu::RESULT_WRITEFAIL;
443
444   return Kumu::RESULT_OK;
445 }
446
447 #else // KM_WIN32
448 //------------------------------------------------------------------------------------------
449 // POSIX
450
451 //
452 Kumu::Result_t
453 Kumu::FileReader::OpenRead(const char* filename) const
454 {
455   KM_TEST_NULL_STR_L(filename);
456   const_cast<FileReader*>(this)->m_Filename = filename;
457   const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
458   return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
459 }
460
461 //
462 Kumu::Result_t
463 Kumu::FileReader::Close() const
464 {
465   if ( m_Handle == -1L )
466     return RESULT_FILEOPEN;
467
468   close(m_Handle);
469   const_cast<FileReader*>(this)->m_Handle = -1L;
470   return RESULT_OK;
471 }
472
473 //
474 Kumu::Result_t
475 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
476 {
477   if ( m_Handle == -1L )
478     return RESULT_FILEOPEN;
479
480   if ( lseek(m_Handle, position, whence) == -1L )
481     return RESULT_BADSEEK;
482
483   return RESULT_OK;
484 }
485
486 //
487 Kumu::Result_t
488 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
489 {
490   KM_TEST_NULL_L(pos);
491
492   if ( m_Handle == -1L )
493     return RESULT_FILEOPEN;
494
495   Kumu::fpos_t tmp_pos;
496
497   if (  (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
498     return RESULT_READFAIL;
499
500   *pos = tmp_pos;
501   return RESULT_OK;
502 }
503
504 //
505 Kumu::Result_t
506 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
507 {
508   KM_TEST_NULL_L(buf);
509   i32_t  tmp_count = 0;
510   ui32_t tmp_int = 0;
511
512   if ( read_count == 0 )
513     read_count = &tmp_int;
514
515   *read_count = 0;
516
517   if ( m_Handle == -1L )
518     return RESULT_FILEOPEN;
519
520   if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
521     return RESULT_READFAIL;
522
523   *read_count = tmp_count;
524   return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
525 }
526
527
528 //------------------------------------------------------------------------------------------
529 //
530
531 //
532 Kumu::Result_t
533 Kumu::FileWriter::OpenWrite(const char* filename)
534 {
535   KM_TEST_NULL_STR_L(filename);
536   m_Filename = filename;
537   m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
538
539   if ( m_Handle == -1L )
540     {
541       DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
542       return RESULT_FILEOPEN;
543     }
544
545   m_IOVec = new h__iovec;
546   return RESULT_OK;
547 }
548
549 //
550 Kumu::Result_t
551 Kumu::FileWriter::OpenModify(const char* filename)
552 {
553   KM_TEST_NULL_STR_L(filename);
554   m_Filename = filename;
555   m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
556
557   if ( m_Handle == -1L )
558     {
559       DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
560       return RESULT_FILEOPEN;
561     }
562
563   m_IOVec = new h__iovec;
564   return RESULT_OK;
565 }
566
567 //
568 Kumu::Result_t
569 Kumu::FileWriter::Writev(ui32_t* bytes_written)
570 {
571   assert( ! m_IOVec.empty() );
572   register h__iovec* iov = m_IOVec;
573   ui32_t tmp_int;
574
575   if ( bytes_written == 0 )
576     bytes_written = &tmp_int;
577
578   if ( m_Handle == -1L )
579     return RESULT_STATE;
580
581   int total_size = 0;
582   for ( int i = 0; i < iov->m_Count; i++ )
583     total_size += iov->m_iovec[i].iov_len;
584
585   int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
586   
587   if ( write_size == -1L || write_size != total_size )
588     return RESULT_WRITEFAIL;
589
590   iov->m_Count = 0;
591   *bytes_written = write_size;  
592   return RESULT_OK;
593 }
594
595 //
596 Kumu::Result_t
597 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
598 {
599   KM_TEST_NULL_L(buf);
600   ui32_t tmp_int;
601
602   if ( bytes_written == 0 )
603     bytes_written = &tmp_int;
604
605   if ( m_Handle == -1L )
606     return RESULT_STATE;
607
608   int write_size = write(m_Handle, buf, buf_len);
609
610   if ( write_size == -1L || (ui32_t)write_size != buf_len )
611     return RESULT_WRITEFAIL;
612
613   *bytes_written = write_size;
614   return RESULT_OK;
615 }
616
617
618 #endif // KM_WIN32
619
620 //------------------------------------------------------------------------------------------
621
622
623 //
624 Kumu::Result_t
625 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
626 {
627   fsize_t    fsize = 0;
628   ui32_t     read_size = 0;
629   FileReader File;
630   ByteString ReadBuf;
631
632   KM_TEST_NULL_STR_L(filename);
633
634   Result_t result = File.OpenRead(filename);
635
636   if ( KM_SUCCESS(result) )
637     {
638       fsize = File.Size();
639
640       if ( fsize > (Kumu::fpos_t)max_size )
641         {
642           DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
643           return RESULT_ALLOC;
644         }
645
646       if ( fsize == 0 )
647         {
648           DefaultLogSink().Error("%s: zero file size\n", filename);
649           return RESULT_READFAIL;
650         }
651
652       result = ReadBuf.Capacity((ui32_t)fsize);
653     }
654
655   if ( KM_SUCCESS(result) )
656     result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
657
658   if ( KM_SUCCESS(result) )
659     outString.assign((const char*)ReadBuf.RoData(), read_size);
660
661   return result;
662 }
663
664
665 //
666 Kumu::Result_t
667 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
668 {
669   FileWriter File;
670   ui32_t write_count = 0;
671   KM_TEST_NULL_STR_L(filename);
672
673   Result_t result = File.OpenWrite(filename);
674
675   if ( KM_SUCCESS(result) )
676     result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
677
678   return result;
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 //