fixed unix group permissions
[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(path);
61   KM_TEST_NULL(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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         return RESULT_ALLOC;
633
634       result = ReadBuf.Capacity((ui32_t)fsize);
635     }
636
637   if ( KM_SUCCESS(result) )
638     result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
639
640   if ( KM_SUCCESS(result) )
641     outString.assign((const char*)ReadBuf.RoData(), read_size);
642
643   return result;
644 }
645
646
647 //
648 Kumu::Result_t
649 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
650 {
651   FileWriter File;
652   ui32_t write_count = 0;
653   KM_TEST_NULL_STR(filename);
654
655   Result_t result = File.OpenWrite(filename);
656
657   if ( KM_SUCCESS(result) )
658     result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
659
660   if ( KM_SUCCESS(result) && write_count != inString.length() )
661     return RESULT_WRITEFAIL;
662
663   return RESULT_OK;
664 }
665
666
667 //------------------------------------------------------------------------------------------
668 //
669
670 // Win32 directory scanner
671 //
672 #ifdef KM_WIN32
673
674 //
675 //
676 Result_t
677 Kumu::DirScanner::Open(const char* filename)
678 {
679   KM_TEST_NULL_STR(filename);
680
681   // we need to append a '*' to read the entire directory
682   ui32_t fn_len = strlen(filename); 
683   char* tmp_file = (char*)malloc(fn_len + 8);
684
685   if ( tmp_file == 0 )
686     return RESULT_ALLOC;
687
688   strcpy(tmp_file, filename);
689   char* p = &tmp_file[fn_len] - 1;
690
691   if ( *p != '/' && *p != '\\' )
692     {
693       p++;
694       *p++ = '/';
695     }
696
697   *p++ = '*';
698   *p = 0;
699   // whew...
700
701   m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
702   Result_t result = RESULT_OK;
703
704   if ( m_Handle == -1 )
705     result = RESULT_NOT_FOUND;
706
707   return result;
708 }
709
710
711 //
712 //
713 Result_t
714 Kumu::DirScanner::Close()
715 {
716   if ( m_Handle == -1 )
717     return RESULT_FILEOPEN;
718
719   if ( _findclose((long)m_Handle) == -1 )
720     return RESULT_FAIL;
721
722   m_Handle = -1;
723   return RESULT_OK;
724 }
725
726
727 // This sets filename param to the same per-instance buffer every time, so
728 // the value will change on the next call
729 Result_t
730 Kumu::DirScanner::GetNext(char* filename)
731 {
732   KM_TEST_NULL(filename);
733
734   if ( m_Handle == -1 )
735     return RESULT_FILEOPEN;
736
737   if ( m_FileInfo.name[0] == '\0' )
738     return RESULT_ENDOFFILE;
739
740   strncpy(filename, m_FileInfo.name, MaxFilePath);
741   Result_t result = RESULT_OK;
742
743   if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
744     {
745       m_FileInfo.name[0] = '\0';
746           
747       if ( errno != ENOENT )
748         result = RESULT_FAIL;
749     }
750
751   return result;
752 }
753
754
755 #else // KM_WIN32
756
757 // POSIX directory scanner
758
759 //
760 Result_t
761 Kumu::DirScanner::Open(const char* filename)
762 {
763   KM_TEST_NULL_STR(filename);
764
765   Result_t result = RESULT_OK;
766
767   if ( ( m_Handle = opendir(filename) ) == NULL )
768     {
769       if ( errno == ENOENT )
770         result = RESULT_ENDOFFILE;
771
772       else
773         result = RESULT_FAIL;
774     }
775
776   return result;
777 }
778
779
780 //
781 Result_t
782 Kumu::DirScanner::Close()
783 {
784   if ( m_Handle == NULL )
785     return RESULT_FILEOPEN;
786
787   if ( closedir(m_Handle) == -1 )
788     return RESULT_FAIL;
789
790   m_Handle = NULL;
791   return RESULT_OK;
792 }
793
794
795 //
796 Result_t
797 Kumu::DirScanner::GetNext(char* filename)
798 {
799   KM_TEST_NULL(filename);
800
801   if ( m_Handle == NULL )
802     return RESULT_FILEOPEN;
803
804   struct dirent* entry;
805
806   for (;;)
807     {
808       if ( ( entry = readdir(m_Handle)) == NULL )
809         return RESULT_ENDOFFILE;
810
811       break;
812     }
813
814   strncpy(filename, entry->d_name, MaxFilePath);
815   return RESULT_OK;
816 }
817
818
819 #endif // KM_WIN32
820
821
822
823 //
824 // end KM_fileio.cpp
825 //