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