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