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