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