fixed the make file to include new location of CMakeLists.txt (src)
[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 // Win32 directory scanner
1319 //
1320 #ifdef KM_WIN32
1321
1322 //
1323 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1324
1325 //
1326 //
1327 Result_t
1328 Kumu::DirScanner::Open(const std::string& filename)
1329 {
1330   // we need to append a '*' to read the entire directory
1331   ui32_t fn_len = filename.size(); 
1332   char* tmp_file = (char*)malloc(fn_len + 8);
1333
1334   if ( tmp_file == 0 )
1335     return RESULT_ALLOC;
1336
1337   strcpy(tmp_file, filename.c_str());
1338   char* p = &tmp_file[fn_len] - 1;
1339
1340   if ( *p != '/' && *p != '\\' )
1341     {
1342       p++;
1343       *p++ = '/';
1344     }
1345
1346   *p++ = '*';
1347   *p = 0;
1348   // whew...
1349
1350   m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1351   Result_t result = RESULT_OK;
1352
1353   if ( m_Handle == -1 )
1354     result = RESULT_NOT_FOUND;
1355
1356   return result;
1357 }
1358
1359
1360 //
1361 //
1362 Result_t
1363 Kumu::DirScanner::Close()
1364 {
1365   if ( m_Handle == -1 )
1366     return RESULT_FILEOPEN;
1367
1368   if ( _findclose((long)m_Handle) == -1 )
1369     return RESULT_FAIL;
1370
1371   m_Handle = -1;
1372   return RESULT_OK;
1373 }
1374
1375
1376 // This sets filename param to the same per-instance buffer every time, so
1377 // the value will change on the next call
1378 Result_t
1379 Kumu::DirScanner::GetNext(char* filename)
1380 {
1381   KM_TEST_NULL_L(filename);
1382
1383   if ( m_Handle == -1 )
1384     return RESULT_FILEOPEN;
1385
1386   if ( m_FileInfo.name[0] == '\0' )
1387     return RESULT_ENDOFFILE;
1388
1389   strncpy(filename, m_FileInfo.name, MaxFilePath);
1390   Result_t result = RESULT_OK;
1391
1392   if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1393     {
1394       m_FileInfo.name[0] = '\0';
1395           
1396       if ( errno != ENOENT )
1397         result = RESULT_FAIL;
1398     }
1399
1400   return result;
1401 }
1402
1403
1404 //
1405 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1406
1407 //
1408 Result_t
1409 Kumu::DirScannerEx::Open(const std::string& dirname)
1410 {
1411   Kumu::DefaultLogSink().Critical("Kumu::DirScannerEx unimplemented for Win32 API.\n");
1412   return RESULT_NOTIMPL;
1413 }
1414
1415 //
1416 Result_t
1417 Kumu::DirScannerEx::Close()
1418 {
1419   Kumu::DefaultLogSink().Critical("Kumu::DirScannerEx unimplemented for Win32 API.\n");
1420   return RESULT_NOTIMPL;
1421 }
1422
1423 //
1424 Result_t
1425 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1426 {
1427   Kumu::DefaultLogSink().Critical("Kumu::DirScannerEx unimplemented for Win32 API.\n");
1428   return RESULT_NOTIMPL;
1429 }
1430
1431 #else // KM_WIN32
1432
1433 // POSIX directory scanner
1434
1435 //
1436 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1437
1438 //
1439 Result_t
1440 Kumu::DirScanner::Open(const std::string& dirname)
1441 {
1442   Result_t result = RESULT_OK;
1443
1444   if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1445     {
1446       switch ( errno )
1447         {
1448         case ENOENT:
1449         case ENOTDIR:
1450           result = RESULT_NOTAFILE;
1451         case EACCES:
1452           result = RESULT_NO_PERM;
1453         case ELOOP:
1454         case ENAMETOOLONG:
1455           result = RESULT_PARAM;
1456         case EMFILE:
1457         case ENFILE:
1458           result = RESULT_STATE;
1459         default:
1460           DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1461           result = RESULT_FAIL;
1462         }
1463     }
1464
1465   return result;
1466 }
1467
1468
1469 //
1470 Result_t
1471 Kumu::DirScanner::Close()
1472 {
1473   if ( m_Handle == NULL )
1474     return RESULT_FILEOPEN;
1475
1476   if ( closedir(m_Handle) == -1 ) {
1477     switch ( errno )
1478       {
1479       case EBADF:
1480       case EINTR:
1481         return RESULT_STATE;
1482       default:
1483         DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1484         return RESULT_FAIL;
1485       }
1486   }
1487
1488   m_Handle = NULL;
1489   return RESULT_OK;
1490 }
1491
1492
1493 //
1494 Result_t
1495 Kumu::DirScanner::GetNext(char* filename)
1496 {
1497   KM_TEST_NULL_L(filename);
1498
1499   if ( m_Handle == NULL )
1500     return RESULT_FILEOPEN;
1501
1502   struct dirent* entry;
1503
1504   for (;;)
1505     {
1506       if ( ( entry = readdir(m_Handle)) == NULL )
1507         return RESULT_ENDOFFILE;
1508
1509       break;
1510     }
1511
1512   strncpy(filename, entry->d_name, MaxFilePath);
1513   return RESULT_OK;
1514 }
1515
1516 //------------------------------------------------------------------------------------------
1517
1518 //
1519 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1520
1521 //
1522 Result_t
1523 Kumu::DirScannerEx::Open(const std::string& dirname)
1524 {
1525   Result_t result = RESULT_OK;
1526
1527   if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1528     {
1529       switch ( errno )
1530         {
1531         case ENOENT:
1532         case ENOTDIR:
1533           result = RESULT_NOTAFILE;
1534         case EACCES:
1535           result = RESULT_NO_PERM;
1536         case ELOOP:
1537         case ENAMETOOLONG:
1538           result = RESULT_PARAM;
1539         case EMFILE:
1540         case ENFILE:
1541           result = RESULT_STATE;
1542         default:
1543           DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1544           result = RESULT_FAIL;
1545         }
1546     }
1547
1548   if ( KM_SUCCESS(result) )
1549     m_Dirname = dirname;
1550
1551   KM_RESULT_STATE_TEST_IMPLICIT();
1552   return result;
1553 }
1554
1555 //
1556 Result_t
1557 Kumu::DirScannerEx::Close()
1558 {
1559   if ( m_Handle == NULL )
1560     return RESULT_FILEOPEN;
1561
1562   if ( closedir(m_Handle) == -1 )
1563     {
1564       switch ( errno )
1565         {
1566         case EBADF:
1567         case EINTR:
1568           KM_RESULT_STATE_HERE();
1569           return RESULT_STATE;
1570
1571         default:
1572           DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1573           return RESULT_FAIL;
1574         }
1575     }
1576
1577   m_Handle = 0;
1578   return RESULT_OK;
1579 }
1580
1581 //
1582 Result_t
1583 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1584 {
1585   if ( m_Handle == 0 )
1586     return RESULT_FILEOPEN;
1587
1588 #if defined(__sun) && defined(__SVR4)
1589   struct stat s;
1590 #endif
1591   struct dirent* entry;
1592
1593   for (;;)
1594     {
1595       if ( ( entry = readdir(m_Handle) ) == 0 )
1596         return RESULT_ENDOFFILE;
1597
1598       break;
1599     }
1600
1601   next_item_name.assign(entry->d_name, strlen(entry->d_name));
1602
1603 #if defined(__sun) && defined(__SVR4)
1604
1605   stat(entry->d_name, &s);
1606
1607   switch ( s.st_mode )
1608     {
1609     case S_IFDIR:
1610       next_item_type = DET_DIR;
1611       break;
1612
1613     case S_IFREG:
1614       next_item_type = DET_FILE;
1615       break;
1616
1617     case S_IFLNK:
1618       next_item_type = DET_LINK;
1619       break;
1620
1621     default:
1622       next_item_type = DET_DEV;
1623     }
1624 #else // __sun 
1625   switch ( entry->d_type )
1626     {
1627     case DT_DIR:
1628       next_item_type = DET_DIR;
1629       break;
1630
1631     case DT_REG:
1632       next_item_type = DET_FILE;
1633       break;
1634
1635     case DT_LNK:
1636       next_item_type = DET_LINK;
1637       break;
1638
1639     default:
1640       next_item_type = DET_DEV;
1641     }
1642 #endif // __sun
1643   return RESULT_OK;
1644 }
1645
1646 #endif // KM_WIN32
1647
1648
1649 //------------------------------------------------------------------------------------------
1650
1651 //
1652 // Attention Windows users: make sure to use the proper separator character
1653 // with these functions.
1654 //
1655
1656 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1657 //
1658 Result_t
1659 Kumu::CreateDirectoriesInPath(const std::string& Path)
1660 {
1661   bool abs = PathIsAbsolute(Path);
1662   PathCompList_t PathComps, TmpPathComps;
1663
1664   PathToComponents(Path, PathComps);
1665
1666   while ( ! PathComps.empty() )
1667     {
1668       TmpPathComps.push_back(PathComps.front());
1669       PathComps.pop_front();
1670       std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1671
1672       if ( ! PathIsDirectory(tmp_path) )
1673         {
1674 #ifdef KM_WIN32
1675           if ( _mkdir(tmp_path.c_str()) != 0 )
1676 #else // KM_WIN32
1677           if ( mkdir(tmp_path.c_str(), 0777) != 0 )
1678 #endif // KM_WIN32
1679             {
1680               DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1681                                      tmp_path.c_str(), strerror(errno));
1682               return RESULT_DIR_CREATE;
1683             }
1684         }
1685     }
1686
1687   return RESULT_OK;
1688 }
1689
1690
1691 //
1692 Result_t
1693 Kumu::DeleteFile(const std::string& filename)
1694 {
1695   if ( _unlink(filename.c_str()) == 0 )
1696     return RESULT_OK;
1697
1698   switch ( errno )
1699     {
1700     case ENOENT:
1701     case ENOTDIR: return RESULT_NOTAFILE;
1702
1703     case EROFS:
1704     case EBUSY:
1705     case EACCES:
1706     case EPERM:   return RESULT_NO_PERM;
1707     }
1708
1709   DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1710   return RESULT_FAIL;
1711 }
1712
1713 //
1714 Result_t
1715 h__DeletePath(const std::string& pathname)
1716 {
1717   if ( pathname.empty() )
1718     return RESULT_NULL_STR;
1719
1720   Result_t result = RESULT_OK;
1721
1722   if ( ! PathIsDirectory(pathname) )
1723     {
1724       result = DeleteFile(pathname);
1725     }
1726   else
1727     {
1728       {
1729         DirScanner TestDir;
1730         char       next_file[Kumu::MaxFilePath];
1731         result = TestDir.Open(pathname.c_str());
1732
1733         while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1734           {
1735             if ( next_file[0] == '.' )
1736               {
1737                 if ( next_file[1] ==  0 )
1738                   continue; // don't delete 'this'
1739                 
1740                 if ( next_file[1] == '.' && next_file[2] ==  0 )
1741                   continue; // don't delete 'this' parent
1742               }
1743
1744             result = h__DeletePath(pathname + std::string("/") + next_file);
1745           }
1746       }
1747
1748       if ( _rmdir(pathname.c_str()) != 0 )
1749         {
1750           switch ( errno )
1751             {
1752             case ENOENT:
1753             case ENOTDIR:
1754               result = RESULT_NOTAFILE;
1755               break;
1756
1757             case EROFS:
1758             case EBUSY:
1759             case EACCES:
1760             case EPERM:
1761               result = RESULT_NO_PERM;
1762               break;
1763
1764             default:
1765               DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1766               result = RESULT_FAIL;
1767             }
1768         }
1769     }
1770
1771   return result;
1772 }
1773
1774 //
1775 Result_t
1776 Kumu::DeletePath(const std::string& pathname)
1777 {
1778   std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1779   DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1780   return h__DeletePath(c_pathname);
1781 }
1782
1783
1784 //
1785 Result_t
1786 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1787 {
1788   DirScanner source_dir;
1789   char next_file[Kumu::MaxFilePath];
1790
1791   Result_t result = source_dir.Open(path);
1792
1793   if ( KM_FAILURE(result) )
1794     return result;
1795
1796   while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1797     {
1798       if ( ( next_file[0] == '.' && next_file[1] == 0 )
1799            || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1800         continue;
1801
1802       return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1803     }
1804
1805   return DeletePath(path);
1806 }
1807
1808
1809 //------------------------------------------------------------------------------------------
1810 //
1811
1812
1813 Result_t
1814 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1815 {
1816 #ifdef KM_WIN32
1817   ULARGE_INTEGER lTotalNumberOfBytes;
1818   ULARGE_INTEGER lTotalNumberOfFreeBytes;
1819
1820   BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1821   if ( fResult )
1822     {
1823       free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1824       total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1825       return RESULT_OK;
1826     }
1827
1828   HRESULT last_error = ::GetLastError();
1829
1830   DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1831   return RESULT_FAIL;
1832 #else // KM_WIN32
1833   struct statfs s;
1834
1835 #if defined(__sun) && defined(__SVR4)
1836   if ( statfs(path.c_str(), &s, s.f_bsize, s.f_fstyp ) == 0 )
1837     {      if ( s.f_blocks < 1 )
1838         {
1839           DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1840                                  path.c_str(), s.f_blocks);
1841           return RESULT_FAIL;
1842         }
1843
1844       free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bfree;
1845       total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1846       return RESULT_OK;
1847     }
1848 #else
1849   if ( statfs(path.c_str(), &s) == 0 )
1850     {
1851       if ( s.f_blocks < 1 )
1852         {
1853           DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1854                                  path.c_str(), s.f_blocks);
1855           return RESULT_FAIL;
1856         }
1857
1858       free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1859       total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1860       return RESULT_OK;
1861     }
1862
1863   switch ( errno )
1864     {
1865     case ENOENT:
1866     case ENOTDIR: return RESULT_NOTAFILE;
1867     case EACCES:  return RESULT_NO_PERM;
1868     }
1869
1870   DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1871   return RESULT_FAIL;
1872 #endif // __sun 
1873 #endif // KM_WIN32
1874
1875
1876
1877 //
1878 // end KM_fileio.cpp
1879 //