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