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