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