Win32 portability fixes
[asdcplib.git] / src / KM_fileio.cpp
1 /*
2 Copyright (c) 2004-2007, 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 #ifdef KM_WIN32
37 #include <direct.h>
38 #endif
39
40 using namespace Kumu;
41
42 #ifdef KM_WIN32
43 typedef struct _stati64 fstat_t;
44 #define S_IFLNK 0
45
46
47 // win32 has WriteFileGather() and ReadFileScatter() but they
48 // demand page alignment and page sizing, making them unsuitable
49 // for use with arbitrary buffer sizes.
50 struct iovec {
51   char* iov_base; // stupid iovec uses char*
52   int   iov_len;
53 };
54 #else
55 #include <sys/uio.h>
56 typedef struct stat     fstat_t;
57 #endif
58
59 //
60 static void
61 split(const std::string& str, char separator, std::list<std::string>& components)
62 {
63   const char* pstr = str.c_str();
64   const char* r = strchr(pstr, separator);
65
66   while ( r != 0 )
67     {
68       assert(r >= pstr);
69       if ( r > pstr )
70         {
71           std::string tmp_str;
72           assert(r - pstr < 100);
73           tmp_str.assign(pstr, (r - pstr));
74           components.push_back(tmp_str);
75         }
76
77       pstr = r + 1;
78       r = strchr(pstr, separator);
79     }
80
81   if( strlen(pstr) > 0 )
82     components.push_back(std::string(pstr));
83 }
84
85
86 //
87 static Kumu::Result_t
88 do_stat(const char* path, fstat_t* stat_info)
89 {
90   KM_TEST_NULL_STR_L(path);
91   KM_TEST_NULL_L(stat_info);
92
93   Kumu::Result_t result = Kumu::RESULT_OK;
94
95 #ifdef KM_WIN32
96   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
97
98   if ( _stati64(path, stat_info) == (__int64)-1 )
99     result = Kumu::RESULT_FILEOPEN;
100
101   ::SetErrorMode( prev );
102 #else
103   if ( stat(path, stat_info) == -1L )
104     result = Kumu::RESULT_FILEOPEN;
105
106   if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
107     result = Kumu::RESULT_FILEOPEN;
108 #endif
109
110   return result;
111 }
112
113 #ifndef KM_WIN32
114
115 //
116 static Kumu::Result_t
117 do_fstat(HANDLE handle, fstat_t* stat_info)
118 {
119   KM_TEST_NULL_L(stat_info);
120
121   Kumu::Result_t result = Kumu::RESULT_OK;
122
123   if ( fstat(handle, stat_info) == -1L )
124     result = Kumu::RESULT_FILEOPEN;
125
126   if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
127     result = Kumu::RESULT_FILEOPEN;
128
129   return result;
130 }
131
132 #endif
133
134
135 //
136 bool
137 Kumu::PathExists(const std::string& pathname)
138 {
139   if ( pathname.empty() )
140     return false;
141
142   fstat_t info;
143
144   if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
145     return true;
146
147   return false;
148 }
149
150 //
151 bool
152 Kumu::PathIsFile(const std::string& pathname)
153 {
154   if ( pathname.empty() )
155     return false;
156
157   fstat_t info;
158
159   if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
160     {
161       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
162         return true;
163     }
164
165   return false;
166 }
167
168
169 //
170 bool
171 Kumu::PathIsDirectory(const std::string& pathname)
172 {
173   if ( pathname.empty() )
174     return false;
175
176   fstat_t info;
177
178   if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
179     {
180       if ( info.st_mode & S_IFDIR )
181         return true;
182     }
183
184   return false;
185 }
186
187 //
188 Kumu::fsize_t
189 Kumu::FileSize(const std::string& pathname)
190 {
191   if ( pathname.empty() )
192     return 0;
193
194   fstat_t info;
195
196   if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
197     {
198       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
199         return(info.st_size);
200     }
201
202   return 0;
203 }
204
205 //
206 static PathCompList_t&
207 s_PathMakeCanonical(PathCompList_t& CList, char separator, bool is_absolute)
208 {
209   PathCompList_t::iterator ci, ri; // component and removal iterators
210
211   for ( ci = CList.begin(); ci != CList.end(); ci++ )
212     {
213       if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
214         {
215           ri = ci++;
216           CList.erase(ri);
217         }
218       else if ( *ci == ".." && ci != CList.begin() )
219         {
220           ri = ci;
221           ri--;
222               
223           if ( *ri != ".." )
224             {
225               CList.erase(ri);
226               ri = ci++;
227               CList.erase(ri);
228             }
229         }
230     }
231
232   return CList;
233 }
234
235 //
236 std::string
237 Kumu::PathMakeCanonical(const std::string& Path, char separator)
238 {
239   PathCompList_t CList;
240   bool is_absolute = PathIsAbsolute(Path, separator);
241   s_PathMakeCanonical(PathToComponents(Path, CList, separator), separator, is_absolute);
242
243   if ( is_absolute )
244     return ComponentsToAbsolutePath(CList, separator);
245
246   return ComponentsToPath(CList, separator);
247 }
248
249 //
250 bool
251 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
252 {
253   return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
254 }
255
256 //
257 Kumu::PathCompList_t&
258 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
259 {
260   split(Path, separator, CList);
261   return CList;
262 }
263
264 //
265 std::string
266 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
267 {
268   if ( CList.empty() )
269     return "";
270
271   PathCompList_t::const_iterator ci = CList.begin();
272   std::string out_path = *ci;
273
274   for ( ci++; ci != CList.end(); ci++ )
275     out_path += separator + *ci;
276
277   return out_path;
278 }
279
280 //
281 std::string
282 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
283 {
284   std::string out_path;
285
286   if ( CList.empty() )
287     out_path = separator;
288   else
289     {
290       PathCompList_t::const_iterator ci;
291
292       for ( ci = CList.begin(); ci != CList.end(); ci++ )
293         out_path += separator + *ci;
294     }
295
296   return out_path;
297 }
298
299 //
300 bool
301 Kumu::PathHasComponents(const std::string& Path, char separator)
302 {
303   if ( strchr(Path.c_str(), separator) == 0 )
304     return false;
305
306   return true;
307 }
308
309 //
310 bool
311 Kumu::PathIsAbsolute(const std::string& Path, char separator)
312 {
313   if ( Path.empty() )
314     return false;
315
316   if ( Path[0] == separator)
317     return true;
318
319   return false;
320 }
321
322 //
323 std::string
324 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
325 {
326   if ( Path.empty() )
327     {
328       std::string out_path;
329       out_path = separator;
330       return out_path;
331     }
332
333   if ( PathIsAbsolute(Path, separator) )
334     return Path;
335
336   char cwd_buf [MaxFilePath];
337   if ( getcwd(cwd_buf, MaxFilePath) == 0 )
338     {
339       DefaultLogSink().Error("Error retrieving current working directory.");
340       return "";
341     }
342
343   PathCompList_t CList;
344   CList.push_back(cwd_buf);
345   CList.push_back(Path);
346
347   return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, separator, true), separator);
348 }
349
350 //
351 std::string
352 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
353 {
354   size_t pos = Path.find(Parent);
355
356   if ( pos == 0 ) // Parent found at offset 0
357     return Path.substr(Parent.size()+1);
358
359   return Path;
360 }
361
362 //
363 std::string
364 Kumu::PathBasename(const std::string& Path, char separator)
365 {
366   PathCompList_t CList;
367   PathToComponents(Path, CList, separator);
368
369   if ( CList.empty() )
370     return "";
371
372   return CList.back();
373 }
374
375 //
376 std::string
377 Kumu::PathDirname(const std::string& Path, char separator)
378 {
379   PathCompList_t CList;
380   bool is_absolute = PathIsAbsolute(Path, separator);
381   PathToComponents(Path, CList, separator);
382
383   if ( CList.empty() )
384     return is_absolute ? "/" : "";
385
386   CList.pop_back();
387
388   if ( is_absolute )
389     return ComponentsToAbsolutePath(CList, separator);
390
391   return ComponentsToPath(CList, separator);
392 }
393
394 //
395 std::string
396 Kumu::PathGetExtension(const std::string& Path)
397 {
398   std::string Basename = PathBasename(Path);
399   const char* p = strrchr(Basename.c_str(), '.'); 
400
401   if ( p++ == 0 )
402     return "";
403
404   return p;
405 }
406
407 //
408 std::string
409 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
410 {
411   std::string Basename = PathBasename(Path);
412   const char* p = strrchr(Basename.c_str(), '.'); 
413
414   if ( p != 0 )
415     Basename = Basename.substr(0, p - Basename.c_str());
416
417   if ( Extension.empty() )
418     return Basename;
419
420   return Basename + "." + Extension;
421 }
422
423 //
424 Kumu::PathList_t&
425 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
426                   Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
427 {
428   PathList_t::const_iterator si;
429   for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
430     {
431       FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
432
433       if ( one_shot && ! FoundPaths.empty() )
434         break;
435     }
436
437   return FoundPaths;
438 }
439
440 //
441 Kumu::PathList_t&
442 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
443                   Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
444 {
445   char name_buf[MaxFilePath];
446   DirScanner Dir;
447
448   if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
449     {
450       while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
451         {
452           if ( name_buf[0] == '.' ) continue; // no hidden files
453           std::string tmp_path = SearchDir + separator + name_buf;
454
455           if ( PathIsDirectory(tmp_path.c_str()) )
456             FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
457           
458           else if ( Pattern.Match(name_buf) )
459             {
460               FoundPaths.push_back(SearchDir + separator + name_buf);
461               if ( one_shot )
462                 break;
463             }
464         }
465     }
466
467   return FoundPaths;
468 }
469
470
471 #ifndef KM_WIN32
472
473 //
474 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
475 {
476   int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
477
478   if ( result )
479     {
480       char buf[128];
481       regerror(result, &m_regex, buf, 128);
482       DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
483       regfree(&m_regex);
484     }
485 }
486
487 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) {
488   m_regex = rhs.m_regex;
489 }
490
491 Kumu::PathMatchRegex::~PathMatchRegex() {
492   regfree(&m_regex);
493 }
494
495 bool
496 Kumu::PathMatchRegex::Match(const std::string& s) const {
497   return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
498 }
499
500
501
502 //
503 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
504 {
505   std::string regex; // convert glob to regex
506
507   for ( const char* p = glob.c_str(); *p != 0; p++ )
508     {
509       switch (*p)
510         {
511         case '.':  regex += "\\.";  break;
512         case '*':  regex += ".*";   break;
513         case '?':  regex += ".?";   break;
514         default:   regex += *p;
515         }
516     }
517   regex += '$';
518
519   int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
520
521   if ( result )
522     {
523       char buf[128];
524       regerror(result, &m_regex, buf, 128);
525       DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
526       regfree(&m_regex);
527     }
528 }
529
530 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) {
531   m_regex = rhs.m_regex;
532 }
533
534 Kumu::PathMatchGlob::~PathMatchGlob() {
535   regfree(&m_regex);
536 }
537
538 bool
539 Kumu::PathMatchGlob::Match(const std::string& s) const {
540   return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
541 }
542
543 #endif
544
545 //------------------------------------------------------------------------------------------
546 // portable aspects of the file classes
547
548 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
549
550 //
551 class Kumu::FileWriter::h__iovec
552 {
553 public:
554   int            m_Count;
555   struct iovec   m_iovec[IOVecMaxEntries];
556   h__iovec() : m_Count(0) {}
557 };
558
559
560
561 //
562 Kumu::fsize_t
563 Kumu::FileReader::Size() const
564 {
565 #ifdef KM_WIN32
566   return FileSize(m_Filename.c_str());
567 #else
568   fstat_t info;
569
570   if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
571     {
572       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
573         return(info.st_size);
574     }
575 #endif
576
577   return 0;
578 }
579
580 // these are declared here instead of in the header file
581 // because we have a mem_ptr that is managing a hidden class
582 Kumu::FileWriter::FileWriter() {}
583 Kumu::FileWriter::~FileWriter() {}
584
585 //
586 Kumu::Result_t
587 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
588 {
589   assert( ! m_IOVec.empty() );
590   register h__iovec* iov = m_IOVec;
591   KM_TEST_NULL_L(buf);
592
593   if ( iov->m_Count >= IOVecMaxEntries )
594     {
595       DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
596                              IOVecMaxEntries);
597       return RESULT_WRITEFAIL;
598     }
599
600   iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
601   iov->m_iovec[iov->m_Count].iov_len = buf_len;
602   iov->m_Count++;
603
604   return RESULT_OK;
605 }
606
607
608 #ifdef KM_WIN32
609 //------------------------------------------------------------------------------------------
610 //
611
612 Kumu::Result_t
613 Kumu::FileReader::OpenRead(const char* filename) const
614 {
615   KM_TEST_NULL_STR_L(filename);
616   const_cast<FileReader*>(this)->m_Filename = filename;
617   
618   // suppress popup window on error
619   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
620
621   const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
622                           (GENERIC_READ),                // open for reading
623                           FILE_SHARE_READ,               // share for reading
624                           NULL,                          // no security
625                           OPEN_EXISTING,                 // read
626                           FILE_ATTRIBUTE_NORMAL,         // normal file
627                           NULL                           // no template file
628                           );
629
630   ::SetErrorMode(prev);
631
632   return ( m_Handle == INVALID_HANDLE_VALUE ) ?
633     Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
634 }
635
636 //
637 Kumu::Result_t
638 Kumu::FileReader::Close() const
639 {
640   if ( m_Handle == INVALID_HANDLE_VALUE )
641     return Kumu::RESULT_FILEOPEN;
642
643   // suppress popup window on error
644   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
645   BOOL result = ::CloseHandle(m_Handle);
646   ::SetErrorMode(prev);
647   const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
648
649   return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
650 }
651
652 //
653 Kumu::Result_t
654 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
655 {
656   if ( m_Handle == INVALID_HANDLE_VALUE )
657     return Kumu::RESULT_STATE;
658
659   LARGE_INTEGER in;
660   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
661   in.QuadPart = position;
662   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
663   HRESULT LastError = GetLastError();
664   ::SetErrorMode(prev);
665
666   if ( (LastError != NO_ERROR
667         && (in.LowPart == INVALID_SET_FILE_POINTER
668             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
669     return Kumu::RESULT_READFAIL;
670   
671   return Kumu::RESULT_OK;
672 }
673
674 //
675 Kumu::Result_t
676 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
677 {
678   KM_TEST_NULL_L(pos);
679
680   if ( m_Handle == (HANDLE)-1L )
681     return Kumu::RESULT_FILEOPEN;
682
683   LARGE_INTEGER in;
684   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
685   in.QuadPart = (__int64)0;
686   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
687   HRESULT LastError = GetLastError();
688   ::SetErrorMode(prev);
689
690   if ( (LastError != NO_ERROR
691         && (in.LowPart == INVALID_SET_FILE_POINTER
692             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
693     return Kumu::RESULT_READFAIL;
694
695   *pos = (Kumu::fpos_t)in.QuadPart;
696   return Kumu::RESULT_OK;
697 }
698
699 //
700 Kumu::Result_t
701 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
702 {
703   KM_TEST_NULL_L(buf);
704   Result_t result = Kumu::RESULT_OK;
705   DWORD    tmp_count;
706   ui32_t tmp_int;
707
708   if ( read_count == 0 )
709     read_count = &tmp_int;
710
711   *read_count = 0;
712
713   if ( m_Handle == INVALID_HANDLE_VALUE )
714     return Kumu::RESULT_FILEOPEN;
715   
716   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
717   if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
718     result = Kumu::RESULT_READFAIL;
719
720   ::SetErrorMode(prev);
721
722   if ( tmp_count == 0 ) /* EOF */
723     result = Kumu::RESULT_ENDOFFILE;
724
725   if ( KM_SUCCESS(result) )
726     *read_count = tmp_count;
727
728   return result;
729 }
730
731
732
733 //------------------------------------------------------------------------------------------
734 //
735
736 //
737 Kumu::Result_t
738 Kumu::FileWriter::OpenWrite(const char* filename)
739 {
740   KM_TEST_NULL_STR_L(filename);
741   m_Filename = filename;
742   
743   // suppress popup window on error
744   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
745
746   m_Handle = ::CreateFile(filename,
747                           (GENERIC_WRITE|GENERIC_READ),  // open for reading
748                           FILE_SHARE_READ,               // share for reading
749                           NULL,                          // no security
750                           CREATE_ALWAYS,                 // overwrite (beware!)
751                           FILE_ATTRIBUTE_NORMAL,         // normal file
752                           NULL                           // no template file
753                           );
754
755   ::SetErrorMode(prev);
756
757   if ( m_Handle == INVALID_HANDLE_VALUE )
758     return Kumu::RESULT_FILEOPEN;
759   
760   m_IOVec = new h__iovec;
761   return Kumu::RESULT_OK;
762 }
763
764 //
765 Kumu::Result_t
766 Kumu::FileWriter::Writev(ui32_t* bytes_written)
767 {
768   assert( ! m_IOVec.empty() );
769   register h__iovec* iov = m_IOVec;
770   ui32_t tmp_int;
771
772   if ( bytes_written == 0 )
773     bytes_written = &tmp_int;
774
775   if ( m_Handle == INVALID_HANDLE_VALUE )
776     return Kumu::RESULT_STATE;
777
778   *bytes_written = 0;
779   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
780   Result_t result = Kumu::RESULT_OK;
781
782   // AFAIK, there is no writev() equivalent in the win32 API
783   for ( register int i = 0; i < iov->m_Count; i++ )
784     {
785       ui32_t tmp_count = 0;
786       BOOL wr_result = ::WriteFile(m_Handle,
787                                    iov->m_iovec[i].iov_base,
788                                    iov->m_iovec[i].iov_len,
789                                    (DWORD*)&tmp_count,
790                                    NULL);
791
792       if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
793         {
794           result = Kumu::RESULT_WRITEFAIL;
795           break;
796         }
797
798       *bytes_written += tmp_count;
799     }
800
801   ::SetErrorMode(prev);
802   iov->m_Count = 0; // error nor not, all is lost
803
804   return result;
805 }
806
807 //
808 Kumu::Result_t
809 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
810 {
811   KM_TEST_NULL_L(buf);
812   ui32_t tmp_int;
813
814   if ( bytes_written == 0 )
815     bytes_written = &tmp_int;
816
817   if ( m_Handle == INVALID_HANDLE_VALUE )
818     return Kumu::RESULT_STATE;
819
820   // suppress popup window on error
821   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
822   BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
823   ::SetErrorMode(prev);
824
825   if ( result == 0 || *bytes_written != buf_len )
826     return Kumu::RESULT_WRITEFAIL;
827
828   return Kumu::RESULT_OK;
829 }
830
831 #else // KM_WIN32
832 //------------------------------------------------------------------------------------------
833 // POSIX
834
835 //
836 Kumu::Result_t
837 Kumu::FileReader::OpenRead(const char* filename) const
838 {
839   KM_TEST_NULL_STR_L(filename);
840   const_cast<FileReader*>(this)->m_Filename = filename;
841   const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
842   return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
843 }
844
845 //
846 Kumu::Result_t
847 Kumu::FileReader::Close() const
848 {
849   if ( m_Handle == -1L )
850     return RESULT_FILEOPEN;
851
852   close(m_Handle);
853   const_cast<FileReader*>(this)->m_Handle = -1L;
854   return RESULT_OK;
855 }
856
857 //
858 Kumu::Result_t
859 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
860 {
861   if ( m_Handle == -1L )
862     return RESULT_FILEOPEN;
863
864   if ( lseek(m_Handle, position, whence) == -1L )
865     return RESULT_BADSEEK;
866
867   return RESULT_OK;
868 }
869
870 //
871 Kumu::Result_t
872 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
873 {
874   KM_TEST_NULL_L(pos);
875
876   if ( m_Handle == -1L )
877     return RESULT_FILEOPEN;
878
879   Kumu::fpos_t tmp_pos;
880
881   if (  (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
882     return RESULT_READFAIL;
883
884   *pos = tmp_pos;
885   return RESULT_OK;
886 }
887
888 //
889 Kumu::Result_t
890 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
891 {
892   KM_TEST_NULL_L(buf);
893   i32_t  tmp_count = 0;
894   ui32_t tmp_int = 0;
895
896   if ( read_count == 0 )
897     read_count = &tmp_int;
898
899   *read_count = 0;
900
901   if ( m_Handle == -1L )
902     return RESULT_FILEOPEN;
903
904   if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
905     return RESULT_READFAIL;
906
907   *read_count = tmp_count;
908   return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
909 }
910
911
912 //------------------------------------------------------------------------------------------
913 //
914
915 //
916 Kumu::Result_t
917 Kumu::FileWriter::OpenWrite(const char* filename)
918 {
919   KM_TEST_NULL_STR_L(filename);
920   m_Filename = filename;
921   m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
922
923   if ( m_Handle == -1L )
924     {
925       DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
926       return RESULT_FILEOPEN;
927     }
928
929   m_IOVec = new h__iovec;
930   return RESULT_OK;
931 }
932
933 //
934 Kumu::Result_t
935 Kumu::FileWriter::OpenModify(const char* filename)
936 {
937   KM_TEST_NULL_STR_L(filename);
938   m_Filename = filename;
939   m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
940
941   if ( m_Handle == -1L )
942     {
943       DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
944       return RESULT_FILEOPEN;
945     }
946
947   m_IOVec = new h__iovec;
948   return RESULT_OK;
949 }
950
951 //
952 Kumu::Result_t
953 Kumu::FileWriter::Writev(ui32_t* bytes_written)
954 {
955   assert( ! m_IOVec.empty() );
956   register h__iovec* iov = m_IOVec;
957   ui32_t tmp_int;
958
959   if ( bytes_written == 0 )
960     bytes_written = &tmp_int;
961
962   if ( m_Handle == -1L )
963     return RESULT_STATE;
964
965   int total_size = 0;
966   for ( int i = 0; i < iov->m_Count; i++ )
967     total_size += iov->m_iovec[i].iov_len;
968
969   int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
970   
971   if ( write_size == -1L || write_size != total_size )
972     return RESULT_WRITEFAIL;
973
974   iov->m_Count = 0;
975   *bytes_written = write_size;  
976   return RESULT_OK;
977 }
978
979 //
980 Kumu::Result_t
981 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
982 {
983   KM_TEST_NULL_L(buf);
984   ui32_t tmp_int;
985
986   if ( bytes_written == 0 )
987     bytes_written = &tmp_int;
988
989   if ( m_Handle == -1L )
990     return RESULT_STATE;
991
992   int write_size = write(m_Handle, buf, buf_len);
993
994   if ( write_size == -1L || (ui32_t)write_size != buf_len )
995     return RESULT_WRITEFAIL;
996
997   *bytes_written = write_size;
998   return RESULT_OK;
999 }
1000
1001
1002 #endif // KM_WIN32
1003
1004 //------------------------------------------------------------------------------------------
1005
1006
1007 //
1008 Kumu::Result_t
1009 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1010 {
1011   fsize_t    fsize = 0;
1012   ui32_t     read_size = 0;
1013   FileReader File;
1014   ByteString ReadBuf;
1015
1016   KM_TEST_NULL_STR_L(filename);
1017
1018   Result_t result = File.OpenRead(filename);
1019
1020   if ( KM_SUCCESS(result) )
1021     {
1022       fsize = File.Size();
1023
1024       if ( fsize > (Kumu::fpos_t)max_size )
1025         {
1026           DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1027           return RESULT_ALLOC;
1028         }
1029
1030       if ( fsize == 0 )
1031         {
1032           DefaultLogSink().Error("%s: zero file size\n", filename);
1033           return RESULT_READFAIL;
1034         }
1035
1036       result = ReadBuf.Capacity((ui32_t)fsize);
1037     }
1038
1039   if ( KM_SUCCESS(result) )
1040     result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1041
1042   if ( KM_SUCCESS(result) )
1043     outString.assign((const char*)ReadBuf.RoData(), read_size);
1044
1045   return result;
1046 }
1047
1048
1049 //
1050 Kumu::Result_t
1051 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1052 {
1053   FileWriter File;
1054   ui32_t write_count = 0;
1055   KM_TEST_NULL_STR_L(filename);
1056
1057   Result_t result = File.OpenWrite(filename);
1058
1059   if ( KM_SUCCESS(result) )
1060     result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1061
1062   return result;
1063 }
1064
1065
1066 //------------------------------------------------------------------------------------------
1067 //
1068
1069 // Win32 directory scanner
1070 //
1071 #ifdef KM_WIN32
1072
1073 //
1074 //
1075 Result_t
1076 Kumu::DirScanner::Open(const char* filename)
1077 {
1078   KM_TEST_NULL_STR_L(filename);
1079
1080   // we need to append a '*' to read the entire directory
1081   ui32_t fn_len = strlen(filename); 
1082   char* tmp_file = (char*)malloc(fn_len + 8);
1083
1084   if ( tmp_file == 0 )
1085     return RESULT_ALLOC;
1086
1087   strcpy(tmp_file, filename);
1088   char* p = &tmp_file[fn_len] - 1;
1089
1090   if ( *p != '/' && *p != '\\' )
1091     {
1092       p++;
1093       *p++ = '/';
1094     }
1095
1096   *p++ = '*';
1097   *p = 0;
1098   // whew...
1099
1100   m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1101   Result_t result = RESULT_OK;
1102
1103   if ( m_Handle == -1 )
1104     result = RESULT_NOT_FOUND;
1105
1106   return result;
1107 }
1108
1109
1110 //
1111 //
1112 Result_t
1113 Kumu::DirScanner::Close()
1114 {
1115   if ( m_Handle == -1 )
1116     return RESULT_FILEOPEN;
1117
1118   if ( _findclose((long)m_Handle) == -1 )
1119     return RESULT_FAIL;
1120
1121   m_Handle = -1;
1122   return RESULT_OK;
1123 }
1124
1125
1126 // This sets filename param to the same per-instance buffer every time, so
1127 // the value will change on the next call
1128 Result_t
1129 Kumu::DirScanner::GetNext(char* filename)
1130 {
1131   KM_TEST_NULL_L(filename);
1132
1133   if ( m_Handle == -1 )
1134     return RESULT_FILEOPEN;
1135
1136   if ( m_FileInfo.name[0] == '\0' )
1137     return RESULT_ENDOFFILE;
1138
1139   strncpy(filename, m_FileInfo.name, MaxFilePath);
1140   Result_t result = RESULT_OK;
1141
1142   if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1143     {
1144       m_FileInfo.name[0] = '\0';
1145           
1146       if ( errno != ENOENT )
1147         result = RESULT_FAIL;
1148     }
1149
1150   return result;
1151 }
1152
1153
1154 #else // KM_WIN32
1155
1156 // POSIX directory scanner
1157
1158 //
1159 Result_t
1160 Kumu::DirScanner::Open(const char* filename)
1161 {
1162   KM_TEST_NULL_STR_L(filename);
1163
1164   Result_t result = RESULT_OK;
1165
1166   if ( ( m_Handle = opendir(filename) ) == NULL )
1167     {
1168       if ( errno == ENOENT )
1169         result = RESULT_ENDOFFILE;
1170
1171       else
1172         result = RESULT_FAIL;
1173     }
1174
1175   return result;
1176 }
1177
1178
1179 //
1180 Result_t
1181 Kumu::DirScanner::Close()
1182 {
1183   if ( m_Handle == NULL )
1184     return RESULT_FILEOPEN;
1185
1186   if ( closedir(m_Handle) == -1 )
1187     return RESULT_FAIL;
1188
1189   m_Handle = NULL;
1190   return RESULT_OK;
1191 }
1192
1193
1194 //
1195 Result_t
1196 Kumu::DirScanner::GetNext(char* filename)
1197 {
1198   KM_TEST_NULL_L(filename);
1199
1200   if ( m_Handle == NULL )
1201     return RESULT_FILEOPEN;
1202
1203   struct dirent* entry;
1204
1205   for (;;)
1206     {
1207       if ( ( entry = readdir(m_Handle)) == NULL )
1208         return RESULT_ENDOFFILE;
1209
1210       break;
1211     }
1212
1213   strncpy(filename, entry->d_name, MaxFilePath);
1214   return RESULT_OK;
1215 }
1216
1217
1218 #endif // KM_WIN32
1219
1220
1221
1222 //
1223 // end KM_fileio.cpp
1224 //