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