b3adc342e2ba5e4c33df2e71309b7baa8fb5ee98
[asdcplib.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$
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.c_str(), -1, 0, 0);
808   wchar_t* buffer = new wchar_t[wn];
809   if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -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.c_str(), -1, 0, 0);
941   wchar_t* buffer = new wchar_t[wn];
942   if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -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 std::string& filename)
971 {
972   m_Filename = filename;
973
974   // suppress popup window on error
975   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
976
977   int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
978   wchar_t* buffer = new wchar_t[wn];
979   if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0) {
980     delete[] buffer;
981     return Kumu::RESULT_FAIL;
982   }
983
984   m_Handle = ::CreateFileW(buffer,
985                           (GENERIC_WRITE|GENERIC_READ),  // open for reading
986                           FILE_SHARE_READ,               // share for reading
987                           NULL,                          // no security
988                           OPEN_ALWAYS,                   // don't truncate existing
989                           FILE_ATTRIBUTE_NORMAL,         // normal file
990                           NULL                           // no template file
991                           );
992
993   delete[] buffer;
994
995   ::SetErrorMode(prev);
996
997   if ( m_Handle == INVALID_HANDLE_VALUE )
998     return Kumu::RESULT_FILEOPEN;
999
1000   m_IOVec = new h__iovec;
1001   return Kumu::RESULT_OK;
1002 }
1003
1004
1005 //
1006 Kumu::Result_t
1007 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1008 {
1009   assert( ! m_IOVec.empty() );
1010   register h__iovec* iov = m_IOVec;
1011   ui32_t tmp_int;
1012
1013   if ( bytes_written == 0 )
1014     bytes_written = &tmp_int;
1015
1016   if ( m_Handle == INVALID_HANDLE_VALUE )
1017     return Kumu::RESULT_STATE;
1018
1019   *bytes_written = 0;
1020   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1021   Result_t result = Kumu::RESULT_OK;
1022
1023   // AFAIK, there is no writev() equivalent in the win32 API
1024   for ( register int i = 0; i < iov->m_Count; i++ )
1025     {
1026       ui32_t tmp_count = 0;
1027       BOOL wr_result = ::WriteFile(m_Handle,
1028                                    iov->m_iovec[i].iov_base,
1029                                    iov->m_iovec[i].iov_len,
1030                                    (DWORD*)&tmp_count,
1031                                    NULL);
1032
1033       if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1034         {
1035           result = Kumu::RESULT_WRITEFAIL;
1036           break;
1037         }
1038
1039       MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1040       *bytes_written += tmp_count;
1041     }
1042
1043   ::SetErrorMode(prev);
1044   iov->m_Count = 0; // error nor not, all is lost
1045
1046   return result;
1047 }
1048
1049 //
1050 Kumu::Result_t
1051 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1052 {
1053   KM_TEST_NULL_L(buf);
1054   ui32_t tmp_int;
1055
1056   if ( bytes_written == 0 )
1057     bytes_written = &tmp_int;
1058
1059   if ( m_Handle == INVALID_HANDLE_VALUE )
1060     return Kumu::RESULT_STATE;
1061
1062   // suppress popup window on error
1063   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1064   BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1065   ::SetErrorMode(prev);
1066
1067   if ( result == 0 || *bytes_written != buf_len )
1068     return Kumu::RESULT_WRITEFAIL;
1069
1070   MaybeHash(buf, buf_len);
1071
1072   return Kumu::RESULT_OK;
1073 }
1074
1075 #else // KM_WIN32
1076 //------------------------------------------------------------------------------------------
1077 // POSIX
1078
1079 //
1080 Kumu::Result_t
1081 Kumu::FileReader::OpenRead(const std::string& filename) const
1082 {
1083   const_cast<FileReader*>(this)->m_Filename = filename;
1084   const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1085   return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1086 }
1087
1088 //
1089 Kumu::Result_t
1090 Kumu::FileReader::Close() const
1091 {
1092   if ( m_Handle == -1L )
1093     return RESULT_FILEOPEN;
1094
1095   close(m_Handle);
1096   const_cast<FileReader*>(this)->m_Handle = -1L;
1097   return RESULT_OK;
1098 }
1099
1100 //
1101 Kumu::Result_t
1102 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1103 {
1104   if ( m_Handle == -1L )
1105     return RESULT_FILEOPEN;
1106
1107   if ( lseek(m_Handle, position, whence) == -1L )
1108     return RESULT_BADSEEK;
1109
1110   return RESULT_OK;
1111 }
1112
1113 //
1114 Kumu::Result_t
1115 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1116 {
1117   KM_TEST_NULL_L(pos);
1118
1119   if ( m_Handle == -1L )
1120     return RESULT_FILEOPEN;
1121
1122   Kumu::fpos_t tmp_pos;
1123
1124   if (  (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1125     return RESULT_READFAIL;
1126
1127   *pos = tmp_pos;
1128   return RESULT_OK;
1129 }
1130
1131 //
1132 Kumu::Result_t
1133 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1134 {
1135   KM_TEST_NULL_L(buf);
1136   i32_t  tmp_count = 0;
1137   ui32_t tmp_int = 0;
1138
1139   if ( read_count == 0 )
1140     read_count = &tmp_int;
1141
1142   *read_count = 0;
1143
1144   if ( m_Handle == -1L )
1145     return RESULT_FILEOPEN;
1146
1147   if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1148     return RESULT_READFAIL;
1149
1150   *read_count = tmp_count;
1151   return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1152 }
1153
1154
1155 //------------------------------------------------------------------------------------------
1156 //
1157
1158 //
1159 Kumu::Result_t
1160 Kumu::FileWriter::OpenWrite(const std::string& filename)
1161 {
1162   m_Filename = filename;
1163   m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
1164
1165   if ( m_Handle == -1L )
1166     {
1167       DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1168       return RESULT_FILEOPEN;
1169     }
1170
1171   m_IOVec = new h__iovec;
1172   return RESULT_OK;
1173 }
1174
1175 //
1176 Kumu::Result_t
1177 Kumu::FileWriter::OpenModify(const std::string& filename)
1178 {
1179   m_Filename = filename;
1180   m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1181
1182   if ( m_Handle == -1L )
1183     {
1184       DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1185       return RESULT_FILEOPEN;
1186     }
1187
1188   m_IOVec = new h__iovec;
1189   return RESULT_OK;
1190 }
1191
1192 //
1193 Kumu::Result_t
1194 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1195 {
1196   assert( ! m_IOVec.empty() );
1197   register h__iovec* iov = m_IOVec;
1198   ui32_t tmp_int;
1199
1200   if ( bytes_written == 0 )
1201     bytes_written = &tmp_int;
1202
1203   if ( m_Handle == -1L )
1204     return RESULT_STATE;
1205
1206   int total_size = 0;
1207   for ( int i = 0; i < iov->m_Count; i++ )
1208     total_size += iov->m_iovec[i].iov_len;
1209
1210   int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1211   
1212   if ( write_size == -1L || write_size != total_size )
1213     return RESULT_WRITEFAIL;
1214
1215   for (int i = 0; i < iov->m_Count; ++i)
1216     {
1217       MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1218     }
1219
1220   iov->m_Count = 0;
1221   *bytes_written = write_size;  
1222   return RESULT_OK;
1223 }
1224
1225 //
1226 Kumu::Result_t
1227 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1228 {
1229   KM_TEST_NULL_L(buf);
1230   ui32_t tmp_int;
1231
1232   if ( bytes_written == 0 )
1233     bytes_written = &tmp_int;
1234
1235   if ( m_Handle == -1L )
1236     return RESULT_STATE;
1237
1238   int write_size = write(m_Handle, buf, buf_len);
1239   MaybeHash(buf, buf_len);
1240
1241   if ( write_size == -1L || (ui32_t)write_size != buf_len )
1242     return RESULT_WRITEFAIL;
1243
1244   *bytes_written = write_size;
1245   return RESULT_OK;
1246 }
1247
1248
1249 #endif // KM_WIN32
1250
1251 //------------------------------------------------------------------------------------------
1252
1253
1254 //
1255 Kumu::Result_t
1256 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1257 {
1258   fsize_t    fsize = 0;
1259   ui32_t     read_size = 0;
1260   FileReader File;
1261   ByteString ReadBuf;
1262
1263   Result_t result = File.OpenRead(filename);
1264
1265   if ( KM_SUCCESS(result) )
1266     {
1267       fsize = File.Size();
1268
1269       if ( fsize > (Kumu::fpos_t)max_size )
1270         {
1271           DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1272           return RESULT_ALLOC;
1273         }
1274
1275       if ( fsize == 0 )
1276         {
1277           DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1278           return RESULT_READFAIL;
1279         }
1280
1281       result = ReadBuf.Capacity((ui32_t)fsize);
1282     }
1283
1284   if ( KM_SUCCESS(result) )
1285     result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1286
1287   if ( KM_SUCCESS(result) )
1288     outString.assign((const char*)ReadBuf.RoData(), read_size);
1289
1290   return result;
1291 }
1292
1293
1294 //
1295 Kumu::Result_t
1296 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1297 {
1298   FileWriter File;
1299   ui32_t write_count = 0;
1300
1301   Result_t result = File.OpenWrite(filename);
1302
1303   if ( KM_SUCCESS(result) )
1304     result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1305
1306   return result;
1307 }
1308
1309 //------------------------------------------------------------------------------------------
1310
1311
1312 //
1313 Kumu::Result_t
1314 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1315 {
1316   ByteString Buffer;
1317   ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1318   Result_t result = Buffer.Capacity(file_size);
1319
1320   if ( KM_SUCCESS(result) )
1321     {
1322       ui32_t read_count = 0;
1323       FileWriter Reader;
1324
1325       result = Reader.OpenRead(Filename);
1326
1327       if ( KM_SUCCESS(result) )
1328         result = Reader.Read(Buffer.Data(), file_size, &read_count);
1329     
1330       if ( KM_SUCCESS(result) )
1331         {
1332           assert(file_size == read_count);
1333           Buffer.Length(read_count);
1334           MemIOReader MemReader(&Buffer);
1335           result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1336         }
1337     }
1338
1339   return result;
1340 }
1341
1342 //
1343 Kumu::Result_t
1344 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1345 {
1346   ByteString Buffer;
1347   Result_t result = Buffer.Capacity(Object.ArchiveLength());
1348
1349   if ( KM_SUCCESS(result) )
1350     {
1351       ui32_t write_count = 0;
1352       FileWriter Writer;
1353       MemIOWriter MemWriter(&Buffer);
1354
1355       result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1356
1357       if ( KM_SUCCESS(result) )
1358         {
1359           Buffer.Length(MemWriter.Length());
1360           result = Writer.OpenWrite(Filename);
1361         }
1362
1363       if ( KM_SUCCESS(result) )
1364         result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1365     }
1366
1367   return result;
1368 }
1369
1370 //------------------------------------------------------------------------------------------
1371 //
1372
1373 //
1374 Result_t
1375 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1376 {
1377   ui32_t file_size = FileSize(Filename);
1378   Result_t result = Buffer.Capacity(file_size);
1379
1380   if ( KM_SUCCESS(result) )
1381     {
1382       ui32_t read_count = 0;
1383       FileWriter Reader;
1384
1385       result = Reader.OpenRead(Filename);
1386
1387       if ( KM_SUCCESS(result) )
1388         result = Reader.Read(Buffer.Data(), file_size, &read_count);
1389     
1390       if ( KM_SUCCESS(result) )
1391         {
1392           if ( file_size != read_count) 
1393             return RESULT_READFAIL;
1394
1395           Buffer.Length(read_count);
1396         }
1397     }
1398   
1399   return result;
1400 }
1401
1402 //
1403 Result_t
1404 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1405 {
1406   ui32_t write_count = 0;
1407   FileWriter Writer;
1408
1409   Result_t result = Writer.OpenWrite(Filename);
1410
1411   if ( KM_SUCCESS(result) )
1412     result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1413
1414   if ( KM_SUCCESS(result) && Buffer.Length() != write_count) 
1415     return RESULT_WRITEFAIL;
1416
1417   return result;
1418 }
1419
1420 //------------------------------------------------------------------------------------------
1421 //
1422
1423 Kumu::DirScanner::DirScanner()
1424   : _iterator(boost::filesystem::directory_iterator())
1425 {
1426 }
1427
1428
1429 Result_t
1430 Kumu::DirScanner::Open(const std::string& filename)
1431 {
1432   if (!boost::filesystem::is_directory(filename))
1433     return RESULT_NOT_FOUND;
1434
1435   _iterator = boost::filesystem::directory_iterator(filename);
1436   return RESULT_OK;
1437 }
1438
1439
1440 //
1441 Result_t
1442 Kumu::DirScanner::GetNext(char* filename)
1443 {
1444   if (_iterator == boost::filesystem::directory_iterator())
1445     return RESULT_ENDOFFILE;
1446
1447 #if BOOST_FILESYSTEM_VERSION == 3
1448   std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1449 #else
1450   std::string f = boost::filesystem::path(*_iterator).filename();
1451 #endif
1452   strncpy(filename, f.c_str(), MaxFilePath);
1453   ++_iterator;
1454   return RESULT_OK;
1455 }
1456
1457 //------------------------------------------------------------------------------------------
1458
1459 //
1460 // Attention Windows users: make sure to use the proper separator character
1461 // with these functions.
1462 //
1463
1464 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1465 //
1466 Result_t
1467 Kumu::CreateDirectoriesInPath(const std::string& Path)
1468 {
1469   bool abs = PathIsAbsolute(Path);
1470   PathCompList_t PathComps, TmpPathComps;
1471
1472   PathToComponents(Path, PathComps);
1473
1474   while ( ! PathComps.empty() )
1475     {
1476       TmpPathComps.push_back(PathComps.front());
1477       PathComps.pop_front();
1478       std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1479
1480       if ( ! PathIsDirectory(tmp_path) )
1481         {
1482 #ifdef KM_WIN32
1483           if ( _mkdir(tmp_path.c_str()) != 0 )
1484 #else // KM_WIN32
1485           if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1486 #endif // KM_WIN32
1487             {
1488               DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1489                                      tmp_path.c_str(), strerror(errno));
1490               return RESULT_DIR_CREATE;
1491             }
1492         }
1493     }
1494
1495   return RESULT_OK;
1496 }
1497
1498
1499 //
1500 Result_t
1501 Kumu::DeleteFile(const std::string& filename)
1502 {
1503   if ( _unlink(filename.c_str()) == 0 )
1504     return RESULT_OK;
1505
1506   switch ( errno )
1507     {
1508     case ENOENT:
1509     case ENOTDIR: return RESULT_NOTAFILE;
1510
1511     case EROFS:
1512     case EBUSY:
1513     case EACCES:
1514     case EPERM:   return RESULT_NO_PERM;
1515     }
1516
1517   DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1518   return RESULT_FAIL;
1519 }
1520
1521 //
1522 Result_t
1523 h__DeletePath(const std::string& pathname)
1524 {
1525   if ( pathname.empty() )
1526     return RESULT_NULL_STR;
1527
1528   Result_t result = RESULT_OK;
1529
1530   if ( ! PathIsDirectory(pathname) )
1531     {
1532       result = DeleteFile(pathname);
1533     }
1534   else
1535     {
1536       {
1537         DirScanner TestDir;
1538         char       next_file[Kumu::MaxFilePath];
1539         result = TestDir.Open(pathname.c_str());
1540
1541         while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1542           {
1543             if ( next_file[0] == '.' )
1544               {
1545                 if ( next_file[1] ==  0 )
1546                   continue; // don't delete 'this'
1547                 
1548                 if ( next_file[1] == '.' && next_file[2] ==  0 )
1549                   continue; // don't delete 'this' parent
1550               }
1551
1552             result = h__DeletePath(pathname + std::string("/") + next_file);
1553           }
1554       }
1555
1556       if ( _rmdir(pathname.c_str()) != 0 )
1557         {
1558           switch ( errno )
1559             {
1560             case ENOENT:
1561             case ENOTDIR:
1562               result = RESULT_NOTAFILE;
1563               break;
1564
1565             case EROFS:
1566             case EBUSY:
1567             case EACCES:
1568             case EPERM:
1569               result = RESULT_NO_PERM;
1570               break;
1571
1572             default:
1573               DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1574               result = RESULT_FAIL;
1575             }
1576         }
1577     }
1578
1579   return result;
1580 }
1581
1582 //
1583 Result_t
1584 Kumu::DeletePath(const std::string& pathname)
1585 {
1586   std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1587   DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1588   return h__DeletePath(c_pathname);
1589 }
1590
1591
1592 //
1593 Result_t
1594 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1595 {
1596   DirScanner source_dir;
1597   char next_file[Kumu::MaxFilePath];
1598
1599   Result_t result = source_dir.Open(path);
1600
1601   if ( KM_FAILURE(result) )
1602     return result;
1603
1604   while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1605     {
1606       if ( ( next_file[0] == '.' && next_file[1] == 0 )
1607            || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1608         continue;
1609
1610       return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1611     }
1612
1613   return DeletePath(path);
1614 }
1615
1616
1617 //------------------------------------------------------------------------------------------
1618 //
1619
1620
1621 Result_t
1622 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1623 {
1624 #ifdef KM_WIN32
1625   ULARGE_INTEGER lTotalNumberOfBytes;
1626   ULARGE_INTEGER lTotalNumberOfFreeBytes;
1627
1628   BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1629   if ( fResult )
1630     {
1631       free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1632       total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1633       return RESULT_OK;
1634     }
1635
1636   HRESULT last_error = ::GetLastError();
1637
1638   DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1639   return RESULT_FAIL;
1640 #else // KM_WIN32
1641   struct statfs s;
1642
1643   if ( statfs(path.c_str(), &s) == 0 )
1644     {
1645       if ( s.f_blocks < 1 )
1646         {
1647           DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1648                                  path.c_str(), s.f_blocks);
1649           return RESULT_FAIL;
1650         }
1651
1652       free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1653       total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1654       return RESULT_OK;
1655     }
1656
1657   switch ( errno )
1658     {
1659     case ENOENT:
1660     case ENOTDIR: return RESULT_NOTAFILE;
1661     case EACCES:  return RESULT_NO_PERM;
1662     }
1663
1664   DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1665   return RESULT_FAIL;
1666 #endif // KM_WIN32
1667
1668
1669
1670 //
1671 // end KM_fileio.cpp
1672 //