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